Alcance
El alcance es el alcance de la función de una variable y función. Todas las variables declaradas en una función en JavaScript siempre son visibles en el cuerpo de la función. Hay ámbitos globales y ámbitos locales en JavaScript, pero no hay alcance de nivel de bloque. La prioridad de las variables locales es mayor que la de las variables globales. A través de varios ejemplos, podemos entender las "reglas no expresadas" de alcance en JavaScript (estas también son preguntas que a menudo se hacen en entrevistas frontales).
1. Declaración variable de antemano
Ejemplo 1:
var scope = "global"; function scopeTest () {console.log (alcance); var Scope = "local"} Scopetest (); //indefinidoLa salida aquí está indefinida y no hay error. Esto se debe a que las declaraciones en la función que mencionamos anteriormente siempre son visibles en el cuerpo de la función. La función anterior es equivalente a:
var scope = "global"; function scopeTest () {var alcance; console.log (alcance); scope = "local"} scopetest (); //localTenga en cuenta que si se olvida VAR, la variable se declara como una variable global.
2. Sin alcance de nivel de bloque
A diferencia de otros idiomas que usamos comúnmente, no hay un alcance de nivel de bloque en JavaScript:
function scopeTest () {var Scope = {}; if (alcance instanceo de objeto) {var j = 1; for (var i = 0; i <10; i ++) {//console.log(i); } console.log (i); // Salida 10} console.log (j); // Salida 1}En JavaScript, el alcance de la función de las variables es el nivel de función, es decir, todas las variables en la función se definen en toda la función, lo que también trae algunas "reglas tácitas" que encontraremos si no tenemos cuidado:
var scope = "Hello"; function scopeTest () {console.log (scope); // ① var scope = "no"; console.log (alcance); // ②}La salida de valor en ① estaba realmente indefinida, lo cual es una locura. Hemos definido el valor de la variable global. ¿No debería ser hola este lugar? De hecho, el código anterior es equivalente a:
var scope = "Hello"; function scopeTest () {var alcance; console.log (alcance); // ① scope = "no"; console.log (alcance); // ②}Las variables tempranas y globales tienen una prioridad menor que las variables locales. Según estas dos reglas, no es difícil entender por qué la salida no está definida.
Cadena de alcance
En JavaScript, cada función tiene su propio contexto de ejecución. Cuando el código se ejecuta en este entorno, se creará una cadena de alcance de objetos variables. La cadena de alcance es una lista de objetos o cadena de objetos, que garantiza un acceso ordenado a objetos variables.
El extremo frontal de la cadena de alcance es el objeto variable del entorno de ejecución del código actual, que a menudo se llama "objeto activo". La búsqueda de variables comienza desde el objeto de la primera cadena. Si el objeto contiene atributos variables, la búsqueda se detendrá. Si no, la búsqueda continuará buscando la cadena de alcance superior hasta que se encuentre el objeto global:
La búsqueda de cadenas de alcance paso a paso también afectará el rendimiento del programa. Cuanto más larga sea la cadena de alcance de las variables, mayor será el impacto en el rendimiento. Esta es también una razón importante por la que tratamos de evitar el uso de variables globales.
Cierre
Conceptos básicos
El alcance es un requisito previo para comprender los cierres. Los cierres se refieren a la capacidad de acceder a variables en el alcance externo dentro del alcance actual.
function createCLosure () {var name = "Jack"; return {setstr: function () {name = "rosa"; }, getStr: function () {nombre de retorno + ": hola"; }}} var builder = new CreateCLosure (); Builder.SetStr (); console.log (builder.getstr ()); // Rose: HolaEl ejemplo anterior devuelve dos cierres en la función, los cuales mantienen referencias al alcance externo, por lo que las variables en la función externa siempre son accesibles donde se llamen. Las funciones definidas dentro de una función agregarán el objeto activo de la función externa a su propia cadena de alcance. Por lo tanto, en el ejemplo anterior, la función interna puede acceder a las propiedades de la función externa a través de la función interna. Esta también es una forma para que JavaScript simule variables privadas.
Nota: Dado que los cierres tendrán ámbitos adicionales de funciones (funciones anónimas internas llevan ámbitos de funciones externas), los cierres ocuparán más espacio de memoria que otras funciones, y el uso excesivo puede conducir a un mayor uso de la memoria.
Variables en cierres
Al usar cierres, debido a la influencia del mecanismo de la cadena de alcance, el cierre solo puede obtener el último valor de la función interna. Un efecto secundario de esto es que si la función interna está en un bucle, el valor de la variable es siempre el último valor.
// Esta instancia no es razonable y tiene ciertos factores de retraso. Esto es principalmente para ilustrar los problemas en la función de bucle de cierre timanage () {for (var i = 0; i <5; i ++) {setTimeout (function () {console.log (i);}, 1000)}; }El programa anterior no ingresa los números 1-5 como esperábamos, sino que genera 5 todo 5 veces. Echemos un vistazo a otro ejemplo:
function createCLosure () {var result = []; for (var i = 0; i <5; i ++) {resultado [i] = function () {return i; }} Resultado de retorno;}Llamar al createCLosure () [0] () devuelve 5, y CreateCLosure () [4] () devuelve el valor sigue siendo 5. De los dos ejemplos anteriores, podemos ver el problema de que los cierres existen cuando se usan funciones internas con bucles: porque la cadena de alcance de cada función almacena objetos activos para funciones externas (Timemanage, createClosure), todos se refieren a la misma variable I. Cuando la función externa regresa, el valor de I en este momento es 5, por lo que el valor de cada función interna I también es 5.
Entonces, ¿cómo resolver este problema? Podemos forzar el retorno del resultado esperado a través de un envoltorio anónimo (expresión de la función auto-ejecutiva anónima):
function timEmange () {for (var i = 0; i <5; i ++) {(function (num) {setTimeOut (function () {console.log (num);}, 1000);}) (i); }}O devolver una asignación de función anónima en la función anónima de cierre:
function timEmemange () {for (var i = 0; i <10; i ++) {setTimeOut ((function (e) {return function () {console.log (e);}}) (i), 1000)}} // timemanager (); Salida 1,2,3,4,5Function CreateCLosure () {var result = []; for (var i = 0; i <5; i ++) {resultado [i] = function (num) {return function () {console.log (num); } }(i); } result de retorno;} // createCLosure () [1] () salida 1; createCLosure () [2] () Salida 2Ya sea que se trate de un envoltorio anónimo o funciones anónimas anónicas, en principio, ya que la función se pasa por valor, el valor de la variable I se copiará a la Num del parámetro real, y se crea una función anónima dentro de la función anónima para devolver el NUM, de modo que cada función tenga una copia de Número que no se afectará entre sí.
Esto en cierre
Preste especial atención al usar esto en cierres, ya que un poco de descuido puede causar problemas. Por lo general, entendemos que este objeto está sujeto a la función en tiempo de ejecución. En la función global, este objeto es un objeto de ventana. Cuando la función se llama como un método en el objeto, esto es igual a este objeto (TODO hace un proceso de clasificación sobre esto). Dado que el alcance de las funciones anónimas es global, este cierre generalmente apunta a la ventana del objeto global:
var scope = "global"; var object = {Scope: "local", getsCope: function () {return function () {return this.scope; }}}Llamar objeto.getscope () () Devuelve el valor global en lugar del local que esperábamos. Dijimos anteriormente que las funciones anónimas internas en el cierre llevarán el alcance de la función externa, entonces, ¿por qué no obtener esto de la función externa? Cuando se llama a cada función, esto y los argumentos se crearán automáticamente. Al buscar funciones anónimas internas, buscan las variables que queremos en el objeto activo. Por lo tanto, deje de buscar funciones externas, y nunca es posible acceder directamente a las variables en funciones externas. En resumen, cuando una función se llama como un método de un objeto en un cierre, es importante prestar especial atención que esto en la función anónima dentro del método apunta a una variable global.
Afortunadamente, podemos resolver este problema de manera muy simple, solo almacene esto en el alcance de la función externa en una variable a la que se puede acceder mediante un cierre:
var scope = "global"; var objeto = {alcance: "local", getsCope: function () {var that = this; Función de retorno () {return that.scope; }}} objeto.getScope () () () Devuelve el valor local.Memoria y rendimiento
Dado que el cierre contiene la misma referencia de la cadena de alcance que el contexto de tiempo de ejecución de la función, tendrá un cierto efecto negativo. Cuando se destruyen el objeto activo y el contexto de tiempo de ejecución en la función, el objeto activo no se puede destruir porque todavía hay una referencia al objeto activo, lo que significa que el cierre ocupa más espacio de memoria que las funciones ordinarias, y también puede causar fuga de memoria en el navegador IE, como sigue:
function bindEvent () {var Target = document.getElementById ("Elem"); Target.OnClick = function () {console.log (target.name); }}En el ejemplo anterior, la función anónima genera una referencia al objetivo del objeto externo. Mientras exista la función anónima, la referencia no desaparecerá, y el objeto objetivo de la función externa no se destruirá, lo que crea una referencia circular. La solución es reducir las referencias circulares a variables externas creando una copia de Target.name y restablecer manualmente el objeto:
function bindEvent () {var Target = document.getElementById ("Elem"); var name = target.name; Target.OnClick = function () {console.log (nombre); } Target = null; }Si hay acceso a variables externas en el cierre, la ruta de búsqueda para identificadores sin duda se agregará, y bajo ciertas circunstancias, esto también causará pérdidas de rendimiento. Hemos mencionado anteriormente: intente almacenar variables externas en variables locales para reducir la longitud de búsqueda de las cadenas de alcance.
Resumen: los cierres no son exclusivos de JavaScript, pero tienen sus propias manifestaciones únicas en JavaScript. Usando cierres, podemos definir algunas variables privadas en JavaScript e incluso imitar los ámbitos de nivel de bloque. Sin embargo, durante el uso de cierres, también necesitamos comprender los problemas existentes para evitar problemas innecesarios.