En JavaScript, deberíamos utilizar variables locales en lugar de variables globales tanto como sea posible. Todo el mundo conoce esta frase, pero ¿quién la dijo primero? ¿Por qué hacer esto? ¿Hay alguna base para esto? Si no hace esto, ¿cuánta pérdida causará en el rendimiento? Este artículo explorará las respuestas a estas preguntas y comprenderá fundamentalmente qué factores están relacionados con el rendimiento de las variables en lectura y escritura.
【Original】Rendimiento variable de JavaScript
【Autor】Nicholas C. Zakas
[Traducción] En JavaScript, ¿por qué deberíamos utilizar variables locales siempre que sea posible?
[Traductor] Mingda
La siguiente es una traducción del texto original:
Sobre la cuestión de cómo mejorar el rendimiento de JavaScript, la sugerencia más escuchada es utilizar variables locales en lugar de variables globales. Este es un consejo que se me quedó grabado y nunca lo cuestioné en mis nueve años de trabajo en desarrollo web, y se basa en el manejo de JavaScript del método de resolución de identificador y alcance.
En primer lugar, debemos dejar claro que las funciones están incorporadas como objetos en JavaScript. El proceso de creación de una función es en realidad el proceso de creación de un objeto. Cada objeto de función tiene una propiedad interna llamada [[Scope]], que contiene la información del alcance cuando se creó la función. De hecho, el atributo [[Scope]] corresponde a una lista de objetos (Objetos variables), y se puede acceder a los objetos de la lista desde la función. Por ejemplo, si creamos una función global A, entonces la propiedad interna [[Scope]] de A contiene solo un objeto global (Objeto Global), y si creamos una nueva función B en A, entonces el atributo [[Scope] ] de B contiene dos objetos, el objeto Objeto de activación de la función A está en el frente y el objeto global (Objeto global) está en la parte posterior.
Cuando se ejecuta una función, se crea automáticamente un objeto ejecutable (Objeto de ejecución) y se vincula a una cadena de alcance (Cadena de alcance). La cadena de alcance se establecerá mediante los siguientes dos pasos para la resolución del identificador.
1. Primero, copie los objetos en las propiedades internas del objeto de función [[Scope]] a la cadena de alcance en orden.
2. En segundo lugar, cuando se ejecuta la función, se creará un nuevo objeto Objeto de activación. Este objeto contiene sus definiciones, parámetros (argumentos) y variables locales (incluidos los parámetros con nombre). Este objeto Objeto de activación se pondrá en acción. El frente de la cadena de dominio.
Durante la ejecución del código JavaScript, cuando se encuentra un identificador, se buscará en la cadena de alcance del contexto de ejecución (Contexto de ejecución) según el nombre del identificador. Comenzando desde el primer objeto en la cadena de alcance (el objeto de activación de la función), si no se encuentra, busque el siguiente objeto en la cadena de alcance, y así sucesivamente, hasta que se encuentre la definición del identificador. Si el último objeto en el alcance, que es el objeto global, no se encuentra después de la búsqueda, se generará un error que le indicará al usuario que la variable no está definida. Este es el modelo de ejecución de funciones y el proceso de resolución de identificadores (Resolución de identificadores) descrito en el estándar ECMA-262. Resulta que la mayoría de los motores JavaScript se implementan de esta manera. Cabe señalar que ECMA-262 no exige el uso de esta estructura, solo describe esta parte de la función.
Después de comprender el proceso de resolución de identificadores (Resolución de identificadores), podemos comprender por qué las variables locales se resuelven más rápido que las variables en otros ámbitos, principalmente porque el proceso de búsqueda se acorta considerablemente. ¿Pero cuánto más rápido será? Para responder a esta pregunta, simulé una serie de pruebas para probar el rendimiento de variables en diferentes profundidades de alcance.
La primera prueba es escribir el valor más simple en una variable (aquí se usa el valor literal 1. El resultado se muestra en la siguiente figura, que es muy interesante:
No es difícil ver en los resultados que cuando el proceso de análisis del identificador requiere una búsqueda profunda, habrá una pérdida de rendimiento y el grado de pérdida de rendimiento aumentará con el aumento de la profundidad del identificador. Como era de esperar, Internet Explorer tuvo el peor desempeño (pero para ser justos, hubo algunas mejoras en IE 8). Vale la pena señalar que aquí hay algunas excepciones: Google Chrome y la última versión de medianoche de WebKit tienen un tiempo de acceso a las variables muy estable y no aumentan al aumentar la profundidad del alcance. Por supuesto, esto debería atribuirse a los motores JavaScript de próxima generación que utilizan, V8 y SquirrelFish. Estos motores realizan optimizaciones al ejecutar código y está claro que estas optimizaciones hacen que el acceso a las variables sea más rápido que nunca. Opera también tuvo un buen desempeño, siendo mucho más rápido que IE, Firefox y la versión actual de Safari, pero más lento que los navegadores basados en V8 y Squirrelfish. El rendimiento de Firefox 3.1 Beta 2 es un poco inesperado. La eficiencia de ejecución de las variables locales es muy alta, pero a medida que aumenta el número de capas de alcance, la eficiencia se reduce considerablemente. Cabe señalar que aquí estoy usando la configuración predeterminada, lo que significa que Firefox no tiene la función de seguimiento activada.
Los resultados anteriores se obtuvieron realizando operaciones de escritura en variables. De hecho, tenía curiosidad por saber si la situación sería diferente al leer variables, así que hice la siguiente prueba. Se descubrió que la velocidad de lectura es ligeramente más rápida que la velocidad de escritura, pero la tendencia de los cambios en el rendimiento es constante.
Al igual que en la prueba anterior, Internet Explorer y Firefox siguieron siendo los más lentos y Opera mostró un rendimiento muy llamativo. Del mismo modo, Chrome y la última versión de Webkit Midnight Edition mostraron tendencias de rendimiento que no tienen nada que ver con la profundidad del alcance. Vale la pena prestar atención. Sí, el tiempo de acceso variable en Firefox 3.1 Beta 2 todavía tiene un salto extraño con la profundidad.
Durante la prueba, descubrí un fenómeno interesante: Chrome tendrá pérdidas de rendimiento adicionales al acceder a variables globales. El tiempo para acceder a las variables globales no tiene nada que ver con el nivel de alcance, pero será un 50% más largo que el tiempo para acceder a las variables locales del mismo nivel.
¿Qué esclarecimientos pueden aportarnos estas dos pruebas? El primero es verificar el antiguo punto de vista, que es utilizar variables locales tanto como sea posible. En todos los navegadores, acceder a las variables locales es más rápido que acceder a las variables entre ámbitos, incluidas las variables globales. Los siguientes puntos deben ser la experiencia obtenida a través de esta prueba:
* Verifique cuidadosamente todas las variables utilizadas en la función. Si hay una variable que no está definida en el alcance actual y se usa más de una vez, entonces debemos guardar esta variable en un. variable local, utilice esta variable local para realizar operaciones de lectura y escritura. Esto puede ayudarnos a reducir la profundidad de búsqueda de variables fuera del alcance a 1. Esto es especialmente importante para las variables globales, porque las variables globales siempre se buscan en la última posición de la cadena de alcance.
* Evite el uso de la declaración with. Porque modificará la cadena de alcance del contexto de ejecución (Contexto de ejecución) y agregará un objeto (Objeto variable) al frente. Esto significa que durante la ejecución de with, las variables locales reales se mueven a la segunda posición en la cadena de alcance, lo que provocará una pérdida de rendimiento.
* Si está seguro de que un fragmento de código definitivamente generará una excepción, evite usar try-catch, porque la rama catch se procesa en la misma cadena de alcance que with. Sin embargo, no hay pérdida de rendimiento en el código de rama de prueba, por lo que aún se recomienda utilizar try-catch para detectar errores impredecibles.
Si desea más discusión sobre este tema, di una pequeña charla en el encuentro de JavaScript de Mountain View el mes pasado. Puede descargar las diapositivas en SlideShare o ver el video completo de la fiesta, que comienza alrededor de los 11 minutos de mi charla.
Notas del traductor:
si tiene alguna duda al leer este artículo, le sugiero que lea los dos artículos siguientes:
* "Modelo de ejecución de modelo de objetos JavaScript" escrito por Richie
* "ECMA-262 Tercera edición", consulte principalmente el Capítulo 10, que es el Contexto de ejecución. Los términos mencionados en este artículo se explican en detalle allí.
Al final, Nicholas mencionó un Meetup de JavaScript de Mountain View. El sitio web de Meetup es en realidad un sitio web de una organización para diversas actividades del mundo real. Hay tantas cosas buenas que vivir en California. actividades para participar. jeje.