2008-11-15 9 views
49

Por ejemplo, ¿la primera parte del código realizará una búsqueda completa dos veces, o es lo suficientemente inteligente como para almacenar en caché los resultados si no se han producido cambios en el DOM?¿jQuery hace algún tipo de almacenamiento en caché de "selectores"?

if ($("#navbar .heading").text() > "") { 
    $("#navbar .heading").hide(); 
} 

y

var $heading = $("#navbar .heading"); 

if ($heading.text() > "") { 
    $heading.hide(); 
} 

Si el selector es más compleja que puedo imaginar es un éxito no trivial.

+0

En mi opinión, debe almacenar en caché selectores únicos, como ID's. También sería bueno si hubiera un complemento que permita el almacenamiento en caché de todos los otros selectores. –

+0

jQuery team lo indicó aquí. https://learn.jquery.com/using-jquery-core/selecting-elements/#saving-selections – trungk18

Respuesta

14

jQuery no es así, pero hay la posibilidad de asignar a las variables dentro de su expresión y luego usar la reutilización de aquellos en expresiones subsecuentes. Por lo tanto, la caché-ifying tu ejemplo ...

if ((cached = $("#navbar .heading")).text() > "") { 
    cached.hide(); 
} 

inconveniente es que hace que el código un poco fuglier y difícil de asimilar.

+5

No tiene que ser así, en muchos casos hace que sea más fácil usar variables para almacenar los elementos seleccionados en grupos nombrados limpiamente. – jondavidjohn

+3

Acabo de escribir un complemento liviano que puede almacenar en caché los selectores, sin dejar el código limpio. https://github.com/farzher/jQuery-Selector -Cache –

+1

Nota: Asegúrese de que usen la palabra clave 'var' al declarar variables para que no sean izadas al ámbito global innecesariamente. – sic1

4

no creo que jquery haga ningún almacenamiento en memoria caché de selectores, sino que confía en xpath/javascript debajo para manejar eso. Dicho esto, hay una serie de optimizaciones que puede utilizar en sus selectores. aquí hay algunos artículos que cubren algunos aspectos básicos:

+2

estaba a punto de modificarse pero ambos enlaces están rotos :-( –

8

No creo que lo hace (aunque no me siento como la lectura a través de tres y medio de miles de líneas de JavaScript en este momento para averiguarlo con certeza).

Sin embargo, lo que está haciendo no necesita varios selectores - esto debería funcionar:

$("#navbar .heading:not(:empty)").hide(); 
13

No es tanto una cuestión de '¿verdad?', Sino '¿puede?', Y no, no puede: puede haber agregado elementos adicionales coincidentes con el DOM desde la última consulta. Esto haría que el resultado de la caché fuera obsoleto, y jQuery no tendría otra forma (sensible) de decir que ejecutar la consulta de nuevo.

Por ejemplo:

$('#someid .someclass').show(); 
$('#someid').append('<div class="someclass">New!</div>'); 
$('#someid .someclass').hide(); 

En este ejemplo, el elemento recién añadido no se oculta si había alguna almacenamiento en caché de la consulta - sería ocultar solamente los elementos que fueron revelados anteriormente.

+5

Podría detectar cambios en el dom entre llamadas e invalidar el caché. Si así es como sin embargo, fue diseñado. –

+0

Suena como más sobrecarga para mí, me imagino que la búsqueda era mucho más abierta a los accesos directos que la monitorización de todo el DOM. Pero wh o sabe? :) – jTresidder

+0

Sí, estuvo de acuerdo, no significaba una exploración completa de DOM, simplemente significaba que si se restringía a cambiar el DOM usando jQuery, podría detectar cambios. –

2

John Resig en su charla Jquery Internals en jQuery Camp 2008 menciona algunos navegadores que admiten eventos que se activan cuando se modifica el DOM. Para tales casos, los resultados de Selctor podrían almacenarse en caché.

12

que acabo de hacer un método para resolver este problema:

var cache = {}; 

function $$(s) 
{ 
    if (cache.hasOwnProperty(s)) 
    { 
     return $(cache[s]); 
    } 

    var e = $(s); 

    if(e.length > 0) 
    { 
     return $(cache[s] = e); 
    } 

} 

y funciona así:

$$('div').each(function(){ ... }); 

Los resultados son precisos en lo que puedo decir, basándose en esta simple comprobación :

console.log($$('#forms .col.r')[0] === $('#forms .col.r')[0]); 

NB, se romperá su aplicación MooTools o cualquier otra biblioteca que utilizaNotación.

+0

Por qué si (e.length> 0). Esto volverá indefinido si no se seleccionan elementos. – benmcdonald

+0

Esto se hace para evitar poblar la memoria caché con datos vacíos. Puedes cambiar fácilmente la lógica. El ejemplo anterior no está destinado a ser utilizado tal cual, sino que se adapta a las necesidades de tu aplicación. También puede observar que no admite todos los argumentos que hace 'jQuery();' original. –

23

¡Siempre guarde en caché sus selecciones!

Es inútil llamar constantemente al $(selector) una y otra vez con el mismo selector.

O casi siempre ... En general, debe mantener una copia en caché del objeto jQuery en una variable local, a menos que espere que haya cambiado o solo lo necesite una vez.

var element = $("#someid"); 

element.click(function() { 

    // no need to re-select #someid since we cached it 
    element.hide(); 
}); 
+6

+1 ...... A.L.W.A.Y.S. ™: D –

+0

pregunta rápida sobre el elemento.hide() ;. Dado que está en un cierre, ¿es mejor hacer element.hide() o $ (this) .hide()? No estoy seguro de cuánto tiempo extra se invierte en envolverlo en un objeto jquery en lugar de rastrear la cadena de alcance. – uriDium

+0

'element.hide()' generalmente es mejor a menos que desee que el cierre funcione en varios elementos y, por lo tanto, lo vincule a '$ (this)'. Aunque si le preocupa el "rendimiento" de cualquiera de estas cosas, está sufriendo un caso de optimizaciones poco realistas. Ni cadenas de alcance, ni '$ (this)' van a ser un cuello de botella en ninguna aplicación. – gnarf

6

Al igual que en su $$ enfoque, he creado una función (del mismo nombre) que utiliza un patrón de memorización para mantener limpia global y también da cuenta de un segundo parámetro de contexto ... como $$ (". clase "," #context "). Esto es necesario si usa la función encadenada find() que ocurre después de que se devuelve $$; por lo tanto, no se almacenará en caché solo a menos que guarde en caché el objeto de contexto primero. También agregué un parámetro booleano al final (segundo o tercer parámetro dependiendo de si usa el contexto) para forzarlo a regresar al DOM.

Código:

function $$(a, b, c){ 
    var key; 
    if(c){ 
     key = a + "," + b; 
     if(!this.hasOwnProperty(key) || c){ 
      this[key] = $(a, b); 
     }   
    } 
    else if(b){ 
     if(typeof b == "boolean"){ 
      key = a; 
      if(!this.hasOwnProperty(key) || b){ 
       this[key] = $(a); 
      } 
     } 
     else{ 
      key = a + "," + b; 
      this[key] = $(a, b); 
     }    
    } 
    else{ 
     key = a; 
     if(!this.hasOwnProperty(key)){ 
      this[key] = $(a); 
     } 
    } 
    return this[key]; 
} 

Uso:

<div class="test">a</div> 
<div id="container"> 
    <div class="test">b</div> 
</div>​ 

<script> 
    $$(".test").append("1"); //default behavior 
    $$(".test", "#container").append("2"); //contextual 
    $$(".test", "#container").append("3"); //uses cache 
    $$(".test", "#container", true).append("4"); //forces back to the dome 
​ 
</script> 
+0

generalmente malo para usar las variables 'a' y' b' o 'x' y' y' en casi todos los idiomas, ya que son nombres de variables internas comúnmente utilizados, pero este es un buen comienzo para un complemento – vol7ron

+1

Para llegar aquí: 'if (! this.hasOwnProperty (clave) || b) {' b siempre debe ser 'true' porque haces' else if (b) '(truey) y luego' if (typeof b == "boolean") ', lo que garantiza que es un booleano. La única verdad booleana es 'verdadera' – vol7ron

+1

Lo mismo para' c' – vol7ron

3

Este $$() funciona bien - debe devolver un objeto jQuery válida en cualquier caso, un no definido.

¡Ten cuidado! Debe/no puede con selectores que dinámicamente pueden cambiar, ej. al agregar nodos que coinciden con el selector o utilizando pseudoclases.

function $$(selector) { 
    return cache.hasOwnProperty(selector) 
    ? cache[selector] 
    : cache[selector] = $(selector); 
}; 

Y $$ podría ser cualquier nombre de función, por supuesto.

1

jQuery Sizzle guarda automáticamente en caché las funciones recientes que se han creado desde los selectores para encontrar elementos DOM. Sin embargo, los elementos en sí mismos no están en la memoria caché.

Además, Sizzle mantiene un caché de las funciones compiladas más recientemente. La memoria caché tiene un tamaño máximo (que se puede ajustar pero tiene un valor predeterminado) para que no se salgan los errores de memoria cuando se usan muchos selectores diferentes.

2

Hay un buen complemento llamado jQache que hace exactamente eso. Después de instalar el complemento, generalmente hago esto:

var $$ = $ .q;

Y a continuación, sólo

$$ ("# barra de navegación .heading") ocultar().;

La mejor parte de todo esto es que también se puede limpiar su caché cuando sea necesario, si está haciendo cosas dinámica, por ejemplo:

$$ ("# barra de navegación .heading", verdadera).esconder(); // vacía el caché y oculta el nuevo #navbar (recién encontrado).en dirección

Y

$$ claro (.); // Borra el caché por completo

+0

¡Agradable! ¿El $$ ("# navbar. Encabezado", verdadero) repobla el caché también? –

+0

¡Sip! Inmediatamente devuelve el nuevo resultado y lo almacena en la memoria caché. Ver el sitio del complemento: tiene cosas aún más avanzadas que yo ni siquiera he usado, como listas de selector (para que pueda agruparlas y borrar solo un tipo específico) etc., pero entrar en él es realmente simple y he descubierto que realmente no necesito las cosas más avanzadas :) – Norris

1

jsPerf se ha reducido hoy, pero this article sugiere que las mejoras de rendimiento de almacenamiento en caché de los selectores de jQuery sería mínimo

enter image description here

Esto puede ser simplemente hacia abajo para el almacenamiento en caché del navegador. El selector probado solo tenía una identificación. Más pruebas se deben hacer para los selectores más complicados y diferentes estructuras de página ...