El alcance es uno de los conceptos más importantes en JavaScript. Si desea aprender bien JavaScript, debe comprender cómo funcionan el alcance y las cadenas de alcance de JavaScript. El artículo de hoy proporciona una breve introducción al alcance y la cadena de alcance de JavaScript, con la esperanza de ayudar a todos a aprender JavaScript mejor.
Alcance de JavaScript
Cualquier lenguaje de programación tiene el concepto de alcance. En pocas palabras, el alcance es el rango accesible de variables y funciones, es decir, el alcance controla la visibilidad y el ciclo de vida de variables y funciones. En JavaScript, existen dos tipos de alcance variable: alcance global y alcance local.
1. Alcance global
Los objetos a los que se puede acceder en cualquier parte del código tienen alcance global. En términos generales, las siguientes situaciones tienen alcance global:
(1) La función más externa y las variables definidas fuera de la función más externa tienen alcance global, por ejemplo:
Copie el código de código de la siguiente manera:
varauthorName="Arroyo de montaña";
funciónhacerAlgo(){
varblogName="";
funcióninnerSay(){
alerta(nombredelblog);
}
interiorSay();
}
alerta(nombre del autor);//Arroyo de la montaña
alert(blogName);//Error de secuencia de comandos
hacer algo();//
internalSay()//error de secuencia de comandos
(2) Todas las variables que no están definidas y asignadas directamente se declaran automáticamente con alcance global, por ejemplo:
Copie el código de código de la siguiente manera:
funciónhacerAlgo(){
varauthorName="Arroyo de montaña";
nombredelblog="";
alerta(nombre del autor);
}
alerta(nombredelblog);//
alert(nombre del autor);//Error de secuencia de comandos
La variable blogName tiene alcance global, pero no se puede acceder a AuthorName fuera de la función.
(3) Todas las propiedades de los objetos de ventana tienen alcance global
Generalmente, las propiedades integradas del objeto de ventana tienen un alcance global, como ventana.nombre, ventana.ubicación, ventana.top, etc.
2. Alcance local
A diferencia del alcance global, generalmente solo se puede acceder al alcance local dentro de un fragmento fijo de código, más comúnmente dentro de una función, por lo que en algunos lugares también verá personas que se refieren a este alcance como un alcance de función, como en el siguiente código Ambos blogName y la función internalSay solo tienen alcance local.
Copie el código de código de la siguiente manera:
funciónhacerAlgo(){
varblogName="";
funcióninnerSay(){
alerta(nombredelblog);
}
interiorSay();
}
alert(blogName);//Error de secuencia de comandos
internalSay();//error de secuencia de comandos
Cadena de alcance
En JavaScript, las funciones también son objetos. De hecho, todo en JavaScript es un objeto. Los objetos de función, al igual que otros objetos, tienen propiedades a las que se puede acceder mediante código y un conjunto de propiedades internas a las que solo puede acceder el motor JavaScript. Una de las propiedades internas es [[Scope]], definida por la tercera edición del estándar ECMA-262. Esta propiedad interna contiene la colección de objetos en el alcance en el que se crea la función. Esta colección se denomina cadena de alcance de la función. , que determina a qué datos puede acceder la función.
Cuando se crea una función, su cadena de alcance se completa con objetos de datos accesibles desde el alcance en el que se creó la función. Por ejemplo, defina la siguiente función:
Copie el código de código de la siguiente manera:
función agregar(núm1,núm2){
varsum=núm1+núm2;
suma de retorno;
}
Cuando se crea la función agregar, su cadena de alcance se completará con un objeto global, que contiene todas las variables globales, como se muestra en la siguiente figura (nota: la imagen solo ilustra una parte de todas las variables):
El alcance de la función add se utilizará durante la ejecución. Por ejemplo, ejecute el siguiente código:
Copie el código de código de la siguiente manera:
var total = agregar(5,10);
Cuando se ejecuta esta función, se crea un objeto interno llamado "contexto de ejecución". El contexto de tiempo de ejecución define el entorno en el que se ejecuta la función. Cada contexto de tiempo de ejecución tiene su propia cadena de alcance para la resolución de identificadores. Cuando se crea un contexto de tiempo de ejecución, su cadena de alcance se inicializa en el objeto contenido en [[Scope]] de la función que se está ejecutando actualmente.
Los valores se copian en la cadena de alcance del contexto de ejecución en el orden en que aparecen en la función. Juntos forman un nuevo objeto llamado "objeto de activación". Este objeto contiene todas las variables locales, parámetros con nombre, colecciones de parámetros y esto de la función. Luego, este objeto se enviará al extremo frontal de la cadena de alcance. se destruye, el objeto activo también se destruye. La nueva cadena de alcance se muestra a continuación:
Durante la ejecución de la función, si no se encuentra una variable, pasará por un proceso de resolución de identificadores para determinar dónde obtener y almacenar los datos. Este proceso comienza desde el encabezado de la cadena de alcance, es decir, desde el objeto activo, y busca un identificador con el mismo nombre. Si lo encuentra, use la variable correspondiente a este identificador. Si no lo encuentra, continúe. busque el siguiente objeto en la cadena de alcance. Si no se encuentran objetos después de la búsqueda, el identificador se considera indefinido. Durante la ejecución de la función, cada identificador se somete a dicho proceso de búsqueda.
Encadenamiento de alcance y optimización de código.
Se puede ver en la estructura de la cadena de alcance que cuanto más profundo esté el identificador en la cadena de alcance del contexto de tiempo de ejecución, más lenta será la velocidad de lectura y escritura. Como se muestra en la figura anterior, debido a que las variables globales siempre existen al final de la cadena de alcance del contexto de tiempo de ejecución, la búsqueda de variables globales es la más lenta durante la resolución del identificador. Por lo tanto, al escribir código, debe utilizar la menor cantidad de variables globales posible y las variables locales tanto como sea posible. Una buena regla general es: si se hace referencia a un objeto de alcance cruzado más de una vez, guárdelo en una variable local antes de usarlo. Por ejemplo el siguiente código:
Copie el código de código de la siguiente manera:
funcióncambiarColor(){
document.getElementById("btnChange").onclick=función(){
document.getElementById("targetCanvas").style.backgroundColor="rojo";
};
}
Esta función hace referencia al documento de variable global dos veces. Para encontrar la variable, debe recorrer toda la cadena de alcance hasta que finalmente se encuentre en el objeto global. Este código se puede reescribir de la siguiente manera:
Copie el código de código de la siguiente manera:
funcióncambiarColor(){
vardoc=documento;
doc.getElementById("btnChange").onclick=función(){
doc.getElementById("targetCanvas").style.backgroundColor="rojo";
};
}
Este código es relativamente simple y no mostrará una gran mejora en el rendimiento después de reescribirlo. Sin embargo, si hay una gran cantidad de variables globales en el programa a las que se accede repetidamente, el rendimiento del código reescrito mejorará significativamente.
cambiar la cadena de alcance
El contexto de ejecución correspondiente es único cada vez que se ejecuta una función, por lo que llamar a la misma función varias veces dará como resultado la creación de múltiples contextos de ejecución. Cuando la función complete la ejecución, el contexto de ejecución se destruirá. Cada contexto de tiempo de ejecución está asociado con una cadena de alcance. En circunstancias normales, durante la ejecución del contexto de tiempo de ejecución, su cadena de alcance solo se verá afectada por la declaración with y la declaración catch.
La declaración with es una forma abreviada de utilizar objetos para evitar escribir código repetido. Por ejemplo:
Copie el código de código de la siguiente manera:
funcióninitUI(){
con(documento){
varbd=cuerpo,
enlaces=getElementsByTagName("a"),
yo = 0,
len=enlaces.longitud;
mientras(i<len){
actualizar(enlaces[i++]);
}
getElementById("btnInit").onclick=función(){
hacer algo();
};
}
}
La declaración de ancho se usa aquí para evitar escribir el documento varias veces, lo que parece más eficiente, pero en realidad causa problemas de rendimiento.
Cuando el código llega a la declaración with, la cadena de alcance del contexto de tiempo de ejecución cambia temporalmente. Se crea un nuevo objeto mutable que contiene todas las propiedades del objeto especificado por el parámetro. Este objeto se colocará al principio de la cadena de alcance, lo que significa que todas las variables locales de la función ahora están en el segundo objeto de la cadena de alcance y, por lo tanto, su acceso será más costoso. Como se muestra a continuación:
Por lo tanto, debe evitar utilizar la instrucción with en su programa. En este ejemplo, simplemente almacenar el documento en una variable local puede mejorar el rendimiento.
Otra cosa que cambia la cadena de alcance es la declaración catch en la declaración try-catch. Cuando ocurre un error en el bloque de código de prueba, el proceso de ejecución salta a la declaración de captura y luego el objeto de excepción se inserta en un objeto mutable y se coloca al principio del alcance. Dentro del bloque catch, todas las variables locales de la función se colocarán en el segundo objeto de la cadena de alcance. Código de muestra:
Copie el código de código de la siguiente manera:
intentar{
hacer algo();
}captura(ex){
alert(ex.message);//La cadena de alcance cambia aquí
}
Tenga en cuenta que una vez que se ejecuta la instrucción catch, la cadena de alcance volverá a su estado anterior. La declaración try-catch es muy útil en la depuración de código y el manejo de excepciones, por lo que no se recomienda evitarla por completo. Puede reducir el impacto en el rendimiento de las declaraciones catch optimizando su código. Un buen patrón es delegar el manejo de errores a una función, por ejemplo:
Copie el código de código de la siguiente manera:
intentar{
hacer algo();
}captura(ex){
handleError(ex);//Delegar al método del procesador
}
En el código optimizado, el método handleError es el único código ejecutado en la cláusula catch. Esta función recibe un objeto de excepción como parámetro, para que pueda manejar los errores de manera más flexible y uniforme. Dado que solo se ejecuta una declaración y no se accede a ninguna variable local, los cambios temporales en la cadena de alcance no afectarán el rendimiento del código.