Cómo los principiantes de jQuery pueden probar y mejorar su código
Spanish (Español) translation by Andrijana Mitevska (you can also view the original English article)
La llegada de jQuery ha hecho que el proceso de escritura de JavaScript sea ridículamente fácil. Pero notarás que hacer pequeños cambios en tu código mejora significativamente la legibilidad y / o el rendimiento. A continuación, se ofrecen algunos consejos que te ayudarán a optimizar tu código.
Configuración de la plataforma
Necesitaremos una plataforma sólida para realizar nuestras pruebas. Aquí está el marcado HTML para la página de prueba en la que ejecutaremos todas nuestras pruebas:
1 |
|
2 |
<!DOCTYPE html> |
3 |
<html lang="en-GB"> |
4 |
|
5 |
<head> |
6 |
<title>Testing out performance enhancements - Siddharth/NetTuts+</title> |
7 |
</head> |
8 |
|
9 |
<body> |
10 |
|
11 |
<div id="container"> |
12 |
<div class="block"> |
13 |
<p id="first"> |
14 |
Some text here |
15 |
</p> |
16 |
<ul id="someList"> |
17 |
<li class="item"></li> |
18 |
<li class="item selected" id="mainItem">Oh, hello there!</li> |
19 |
<li class="item"></li> |
20 |
<li class="item"></li> |
21 |
</ul> |
22 |
</div> |
23 |
</div> |
24 |
|
25 |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> |
26 |
<script> |
27 |
console.profile() ; |
28 |
|
29 |
// Our code here |
30 |
|
31 |
console.profileEnd(); |
32 |
</script> |
33 |
|
34 |
</body> |
35 |
</html> |
No hay nada especial aquí; solo un montón de elementos que podemos apuntar y probar. Estamos usando Firebug para registrar los tiempos aquí. profile comienza el proceso, profileEnd lo detiene y anota cuánto tiempo tomó la tarea. Normalmente uso el método de perfil principal de Firebug, pero para nuestros propósitos tortuosos, esto será suficiente.
1. Detectar si existe un elemento
Como suele ser el caso, estarás sirviendo un solo archivo de secuencia de comandos que contiene tu código en todas las páginas de tu sitio. Suele ser un código que a menudo realiza acciones en elementos inexistentes en la página actual. Aunque jQuery maneja problemas como estos con bastante gracia, esto no significa que puedas simplemente ignorar cualquier problema. De hecho, si llamas a los métodos de jQuery en una colección vacía, no se ejecutarán.
Como práctica recomendada, solo ejecuta código que sea aplicable a la página cargada actualmente, en lugar de agrupar todo tu código en una sola verificación de documento listo y entregarlo al cliente.
Veamos el primer escenario:
1 |
console.profile(); |
2 |
var ele = $("#somethingThatisNotHere");
|
3 |
ele.text("Some text").slideUp(300).addClass("editing");
|
4 |
$("#mainItem");
|
5 |
console.profileEnd(); |
6 |
|
7 |
//Some more awesome, ground shattering code here ._. |
Firebug's escupe el siguiente resultado:

Esta vez, verifiquemos si el elemento en el que estamos buscando realizar acciones existe antes de hacerlo.
1 |
console.profile() ; |
2 |
var ele = $("#somethingThatisNotHere");
|
3 |
if ( ele[0] ) {
|
4 |
ele.text("Some text").slideUp(300).addClass("editing");
|
5 |
} |
6 |
$("#mainItem");
|
7 |
console.profileEnd(); |
8 |
|
9 |
//Some more awesome, ground shattering code here ._. |
Y los resultados:

¿Ves? Es bastante simple, va al grano y hace el trabajo. Ten en cuenta que no es necesario verificar si existe un elemento para cada bit de tu código. Notarás en tu página que ciertas partes más grandes generalmente se beneficiarán de este método. Usa tu juicio aquí.
2. Utiliza los selectores de forma eficaz
Intents usar una identificación en lugar de aprobar una clase.
Este es un tema importante, así que lo mantendré lo más conciso posible. En primer lugar, al pasar selectores, intenta usar una ID en lugar de pasar una clase. jQuery usa directamente el método nativo getElementById para encontrar un elemento por ID, mientras que en el caso de una clase tiene que hacer algo de vudú interno para adquirirlo, al menos en navegadores más antiguos.
Veremos los diferentes selectores que puede usar para apuntar al segundo elemento li. Probaremos cada uno de ellos y cómo modifican el rendimiento.
El primer método, el más fácil, será apuntar directamente a él usando la clase seleccionada. Veamos qué devuelve el generador de perfiles de Firebug.
1 |
console.profile() ; |
2 |
|
3 |
$(".selected");
|
4 |
|
5 |
console.profileEnd(); |

Y el resultado: 0.308ms. A continuación, le ponemos un prefijo a un nombre de etiqueta para limitarlo. De esta manera, podemos reducir nuestra búsqueda apuntando primero solo a los elementos DOM seleccionados, con document.getElementsByTagName.
1 |
console.profile() ; |
2 |
|
3 |
$("li.selected");
|
4 |
|
5 |
console.profileEnd(); |

Y el resultado: 0,291 ms. Aproximadamente 0,02 ms recortado. Esto es insignificante debido al hecho de que estamos probando en Firefox; sin embargo, debe tenerse en cuenta que este aumento de rendimiento será notablemente mayor en los navegadores más antiguos, como Internet Explorer 6.
A continuación, descendemos del ID del elemento padre.
1 |
console.profile() ; |
2 |
|
3 |
$("#someList .selected");
|
4 |
|
5 |
console.profileEnd(); |

Y el resultado: 0,283 ms. Intentemos ser un poco más específicos. También especificamos el tipo de elemento además de la identificación del antepasado.
1 |
console.profile() ; |
2 |
|
3 |
$("#someList li.selected");
|
4 |
|
5 |
console.profileEnd(); |

Y el resultado: 0,275 ms. Otra pequeña parte afeitada. Finalmente, apuntémoslo directamente usando un ID a.
1 |
console.profile() ; |
2 |
|
3 |
$("#mainItem");
|
4 |
|
5 |
console.profileEnd(); |

Y el resultado: 0,165 ms. ¡Impresionante! Esto realmente te muestra cuánto más rápido es ejecutar métodos nativos. Ten en cuenta que mientras los navegadores modernos pueden aprovechar cosas como getElementsByClassName, los navegadores más antiguos no pueden, lo que resulta en un rendimiento mucho más lento. Siempre considera esto al codificar.
3. Ten en cuenta el modelo de análisis de Sizzle y la adición de alcances
Sizzle, el motor de selección que utiliza jQuery, creado por John Resig, analiza los selectores de derecha a izquierda, lo que genera algunas cadenas de análisis inesperadas.
Considera este selector:
1 |
$("#someList .selected");
|
Cuando Sizzle encuentra un selector de este tipo, primero construye la estructura DOM, usando el selector como raíz, descarta los elementos que no tienen la clase requerida y, para cada elemento de la clase, verifica si su padre tiene un ID de someList.
Para tener en cuenta esto, asegúrese de que la parte más a la derecha de su selector sea lo más específica posible. Por ejemplo, al especificar li.selected en lugar de .selected, reduce el número de nodos que tiene que comprobar. Esta es la razón por la que el rendimiento saltó en la sección anterior. Al agregar restricciones adicionales, reduces efectivamente la cantidad de nodos que tiene que verificar.
Para ajustar mejor la forma en que se obtienen los elementos, debe considerar agregar un contexto para cada solicitud.
1 |
var someList = $('#someList')[0];
|
2 |
$(".selected", someList);
|
Al agregar un contexto, la forma en que se busca el elemento cambia por completo. Ahora, el elemento que proporciona el contexto, someList en nuestro caso, se busca primero y, una vez obtenido, se eliminan los elementos secundarios que no tienen la clase requerida.
Ten en cuenta que, en general, es una buena práctica pasar un elemento DOM como contexto de tu selector de jQuery. El uso de un contexto es más útil cuando se almacena en alguna variable. De lo contrario, puedes simplificar el proceso y usar find (), lo que jQuery, en sí mismo, hace bajo el capó.
1 |
$('#someList').find('.selected');
|
Me gustaría decir que el aumento de rendimiento estará claramente definido, pero no puedo. He realizado pruebas en varios navegadores y si el rendimiento del enfoque de alcance supera al de la versión básica depende de varios factores, incluido si el navegador admite métodos específicos.
4. Evita el desperdicio de consultas
Cuando navegas por el código de otra persona, a menudo encontrarás.
1 |
// Other code |
2 |
|
3 |
$(element).doSomething(); |
4 |
|
5 |
// More code |
6 |
|
7 |
$(element).doSomethingElse(); |
8 |
|
9 |
// Even more code |
10 |
|
11 |
$(element).doMoreofSomethingElse(); |
Por favor, no hagas esto. Nunca. El desarrollador está instanciando este "elemento" una y otra vez. Esto es un desperdicio.
Veamos cuánto tiempo tarda en ejecutarse un código tan horrendo.
1 |
console.profile() ; |
2 |
|
3 |
$("#mainItem").hide();
|
4 |
$("#mainItem").val("Hello");
|
5 |
$("#mainItem").html("Oh, hey there!");
|
6 |
$("#mainItem").show();
|
7 |
|
8 |
console.profileEnd(); |

Si el código está estructurado como el anterior, uno tras otro, puedes usar el encadenamiento así:
1 |
console.profile(); |
2 |
|
3 |
$("#mainItem").hide().val("Hello").html("Oh, hey there!").show();
|
4 |
|
5 |
console.profileEnd(); |
Mediante el encadenamiento, se adquiere el elemento transferido inicialmente y se pasa una referencia a cada llamada posterior, lo que reduce el tiempo de ejecución. De lo contrario, se crea un nuevo objeto jQuery cada vez.
Pero si, a diferencia de lo anterior, las secciones que hacen referencia al elemento no son concurrentes, tendrás que almacenar en caché el elemento y luego realizar las mismas operaciones que antes.
1 |
console.profile() ; |
2 |
|
3 |
var elem = $("#mainItem");
|
4 |
|
5 |
elem.hide(); |
6 |
|
7 |
//Some code |
8 |
elem.val("Hello");
|
9 |
|
10 |
//More code |
11 |
elem.html("Oh, hey there!");
|
12 |
|
13 |
//Even more code |
14 |
elem.show(); |
15 |
|
16 |
console.profileEnd(); |

Como se desprende de los resultados, el almacenamiento en caché o el encadenamiento reduce considerablemente el tiempo de ejecución.
5. Realiza la manipulación DOM de forma más inteligente
Sugerir una manipulación DOM no tradicional en mi artículo anterior provocó algunas críticas por parte de algunas personas antes de demostrar que el aumento de rendimiento realmente vale la pena. Ahora lo probaremos nosotros mismos.
Para la prueba, crearemos 50 elementos li, los agregaremos a la lista actual y determinaremos cuánto tiempo lleva.
Primero revisaremos el método normal e ineficiente. Básicamente, agregamos el elemento a la lista cada vez que se ejecuta el ciclo.
1 |
console.profile() ; |
2 |
|
3 |
var list = $("#someList");
|
4 |
|
5 |
for (var i=0; i<50; i++) |
6 |
{
|
7 |
list.append('<li>Item #' + i + '</li>');
|
8 |
} |
9 |
|
10 |
console.profileEnd(); |
Veamos cómo le fue, ¿de acuerdo?

Ahora, seguiremos un camino ligeramente diferente. Básicamente, agregaremos la cadena HTML requerida a una variable primero, y luego solo redistribuiremos el DOM una vez.
1 |
console.profile() ; |
2 |
|
3 |
var list = $("#someList");
|
4 |
var items = ""; |
5 |
|
6 |
for (var i=0; i<50; i++){
|
7 |
items += '<li>Item #' + i + '</li>'; |
8 |
} |
9 |
|
10 |
list.append(items); |
11 |
|
12 |
console.profileEnd(); |

Como era de esperar, el tiempo necesario ha disminuido significativamente.

Si estás usando jQuery como reemplazo de getElementById, pero nunca utilizas ninguno de sus métodos proporcionados, entonces lo estás haciendo mal.
Si deseas llevar las cosas más allá, pregúntate si realmente necesitas crear un nuevo objeto jQuery con el propósito de apuntar a algún elemento. Si estás usando jQuery como reemplazo de document.getElementById, pero nunca utilizas ninguno de sus métodos proporcionados, entonces lo estás haciendo mal. En este caso, podemos salirse con la suya con JS sin formato.
1 |
console.profile() ; |
2 |
|
3 |
var list = document.getElementById('someList');
|
4 |
var items = ''; |
5 |
|
6 |
for (var i=0; i<50; i++){
|
7 |
items += '<li>Item #' + i + '</li>'; |
8 |
} |
9 |
|
10 |
list.innerHTML = items; |
11 |
|
12 |
console.profileEnd(); |
Algunas advertencias
Notarás que la diferencia en el tiempo de ejecución entre el código optimizado y no optimizado está en el rango de una fracción de milisegundo. Esto se debe a que nuestro documento de prueba es muy pequeño con una cantidad increíblemente pequeña de nodos. Una vez que comience a trabajar con sitios de nivel de producción con algunos miles de nodos, realmente se sumará.
También ten en cuenta que en la mayoría de estas pruebas, simplemente accedo a los elementos. Cuando comienzas a aplicarles las funciones adecuadas, aumentará el delta en el tiempo de ejecución.
También entiendo que este no es el método más científico para probar el rendimiento, sin embargo, para tener una idea general de cuánto afecta cada uno de estos cambios al rendimiento, creo que esto es suficiente.
Finalmente, en la mayoría de sus aplicaciones web, la velocidad de conexión y el tiempo de respuesta del servidor web en cuestión jugarán un papel más importante en el rendimiento de su aplicación más que los ajustes en el código que realice. Sin embargo, esta sigue siendo información importante y te ayudará en el futuro cuando intentes obtener el mayor rendimiento posible de tu código.
Eso es todo amigos
Y hemos terminado. Algunos puntos a tener en cuenta cuando intentes optimizar tu código; esta no es la lista completa de ajustes, por supuesto, y es posible que los puntos no se apliquen necesariamente a todas las situaciones. De cualquier manera, estaré observando los comentarios de cerca para leer lo que tienes que decir sobre el tema. ¿Algún error que ves aquí? Envíame una línea a continuación.
¿Preguntas? Cosas bonitas que decir? Críticas? Presiona la sección de comentarios y déjame un comentario. ¡Feliz codificación!



