El alcance, o el conjunto de reglas que determinan dónde viven sus variables, es uno de los conceptos más básicos de cualquier lenguaje de programación. De hecho, es tan fundamental que es fácil olvidar lo sutiles que pueden ser las reglas.!
Comprender exactamente cómo el motor de JavaScript "piensa" sobre el alcance evitará que escriba los errores comunes que puede causar la elevación, lo preparará para envolver su cabeza alrededor de los cierres, y lo acercará mucho más a nunca escribir errores siempre otra vez.
... Bueno, te ayudará a entender la elevación y los cierres, de todos modos.
En este artículo, echaremos un vistazo a:
dejar
y const
cambia el juegoVamos a bucear en.
Si está interesado en obtener más información sobre ES6 y cómo aprovechar la sintaxis y las funciones para mejorar y simplificar su código JavaScript, ¿por qué no echa un vistazo a estos dos cursos?
Si has escrito incluso una línea de JavaScript antes, sabrás que donde tu definir tus variables determinan donde puedes utilizar ellos. El hecho de que la visibilidad de una variable depende de la estructura de su código fuente se llama léxico alcance.
Hay tres formas de crear alcance en JavaScript:
dejar
o const
dentro de un bloque de código. Dichas declaraciones solo son visibles dentro del bloque.. captura
bloquear. Lo creas o no, esta realidad hace crear un nuevo alcance!"uso estricto"; var mr_global = "Mr Global"; function foo () var mrs_local = "Mrs Local"; console.log ("Puedo ver" + mr_global + "y" + mrs_local + "."); barra de funciones () console.log ("También puedo ver" + mr_global + "y" + mrs_local + "."); foo (); // Funciona como se espera intente console.log ("Pero / I / can't see" + mrs_local + "."); catch (err) console.log ("Acaba de obtener un" + err + "."); deja foo = "foo"; barra constante = "barra"; console.log ("Puedo usar" + foo + bar + "en su bloque ..."); prueba console.log ("Pero no fuera de él"); catch (err) console.log ("Acaba de obtener otro" + err + "."); // Lanza un error de referencia! console.log ("Tenga en cuenta que" + err + "no existe fuera de 'catch'!")
El fragmento anterior muestra los tres mecanismos de alcance. Puedes ejecutarlo en Node o Firefox, pero Chrome no funciona bien con dejar
, todavía.
Vamos a hablar de cada uno de estos con un detalle exquisito. Comencemos con una visión detallada de cómo JavaScript determina qué variables pertenecen a qué ámbito.
Cuando ejecutas un fragmento de JavaScript, suceden dos cosas para que funcione..
Durante la Compilacion paso, el motor de JavaScript:
Es solo durante ejecución que el motor de JavaScript realmente establece el valor de las referencias de variable igual a sus valores de asignación. Hasta entonces, son indefinido
.
// Puedo usar first_name en cualquier parte de este programa var first_name = "Peleke"; función emergente (primer nombre) // solo puedo usar el último nombre dentro de esta función var last_name = "Sengstacke"; alerta (first_name + "+ last_name); ventana emergente (first_name);
Veamos lo que hace el compilador..
Primero, lee la línea. var first_name = "Peleke"
. A continuación, determina qué alcance Para guardar la variable en. Debido a que estamos en el nivel superior del script, se da cuenta de que estamos en el alcance global. Entonces, guarda la variable nombre de pila
al alcance global e inicializa su valor para indefinido
.
En segundo lugar, el compilador lee la línea con función emergente (primer nombre)
. Porque el función la palabra clave es lo primero en la línea, crea un nuevo ámbito para la función, registra la definición de la función en el ámbito global y se asoma dentro para encontrar declaraciones de variables.
Efectivamente, el compilador encuentra uno. Desde que tenemos var last_name = "Sengstacke"
En la primera línea de nuestra función, el compilador guarda la variable. apellido
al ámbito de aplicación de surgir
-no al ámbito global y establece su valor en indefinido
.
Dado que no hay más declaraciones de variables dentro de la función, el compilador regresa al ámbito global. Y como no hay más declaraciones de variables. ahí, esta fase está hecha.
Tenga en cuenta que no tenemos en realidad correr nada aún. El trabajo del compilador en este punto es simplemente asegurarse de que sepa el nombre de todos; no le importa qué ellas hacen.
En este punto, nuestro programa sabe que:
nombre de pila
en el ámbito global.surgir
en el ámbito global.apellido
en el ámbito de surgir
.nombre de pila
y apellido
son indefinido
.No importa que hayamos asignado esos valores de variables en otro lugar de nuestro código. El motor se encarga de eso en ejecución.
Durante el siguiente paso, el motor vuelve a leer nuestro código, pero esta vez, ejecuta eso.
Primero, lee la línea., var first_name = "Peleke"
. Para ello, el motor busca la variable llamada. nombre de pila
. Como el compilador ya ha registrado una variable con ese nombre, el motor la encuentra y establece su valor en "Peleke"
.
A continuación, lee la línea., función emergente (primer nombre)
. Ya que no estamos ejecutando La función aquí, el motor no está interesado y salta sobre ella..
Por último, lee la línea. emergente (primer_nombre)
. Desde que nosotros son ejecutando una función aquí, el motor:
surgir
nombre de pila
surgir
como una función, pasando el valor de nombre de pila
como parámetroCuando se ejecuta surgir
, Pasa por este mismo proceso, pero esta vez dentro de la función. surgir
. Eso:
apellido
apellido
valor igual a "Sengstacke"
alerta
, ejecutándolo como una función con "Peleke Sengstacke"
como su parámetroResulta que hay mucho más bajo el capó de lo que podríamos haber pensado!
Ahora que entiende cómo JavaScript lee y ejecuta el código que escribe, estamos listos para abordar algo un poco más cerca de casa: cómo funciona el levantamiento.
Empecemos con un código.
bar(); barra de funciones () si (! foo) alerta (foo + "? Esto es extraño ..."); var foo = "barra"; roto (); // ¡Error de tecleado! var broken = function () alert ("¡Esta alerta no se mostrará!");
Si ejecuta este código, notará tres cosas:
foo
antes de asignarle, pero su valor es indefinido
.roto
antes de definirlo, pero obtendrás un Error de tecleado
.bar
Antes de definirlo, y funciona como se desea..Levantamiento se refiere al hecho de que JavaScript hace que todos nuestros nombres de variables declarados estén disponibles en todos lados en sus ámbitos -incluyendo antes de les asignamos.
Los tres casos en el fragmento son los tres que deberá conocer en su propio código, por lo que los repasaremos uno por uno..
Recuerde, cuando el compilador de JavaScript lee una línea como var foo = "bar"
, eso:
foo
al alcance más cercanofoo
a indefinidoLa razón por la que podemos usar foo
antes de asignárnoslo es porque, cuando el motor busca la variable con ese nombre, hace existe. Por eso no lanza un Error de referencia
.
En su lugar, obtiene el valor indefinido
, y trata de usar eso para hacer lo que le pidas. Por lo general, eso es un error.
Teniendo eso en cuenta, podríamos imaginar que lo que JavaScript ve en nuestra función bar
es más como esto:
barra de funciones () var foo; // indefinido si (! foo) //! indefinido es verdadero, así que alerta (foo + "? Esto es extraño ..."); foo = "barra";
Este es el Primera regla de izamiento, si lo desea: las variables están disponibles a lo largo de su alcance, pero tiene el valor indefinido
hasta que su código les asigne.
Un lenguaje común de JavaScript es escribir todos tus var
Declaraciones en la parte superior de su alcance, en lugar de donde las use por primera vez. Parafraseando a Doug Crockford, esto ayuda a su código leer más como eso carreras.
Cuando lo piensas, eso tiene sentido. Está bastante claro por qué bar
se comporta como lo hace cuando escribimos nuestro código como lo lee JavaScript, ¿no es así? Entonces, ¿por qué no escribir así? todos el tiempo?
El hecho de que tengamos un Error de tecleado
cuando intentamos ejecutar roto
Antes de definirlo, es solo un caso especial de la primera regla.
Definimos una variable, llamada roto
, que el compilador se registra en el ámbito global y establece igual a indefinido
. Cuando intentamos ejecutarlo, el motor busca el valor de roto
, encuentra que es indefinido
, y trata de ejecutar indefinido
como una función.
Obviamente, indefinido
no es Una función es por eso que obtenemos una Error de tecleado
!
Finalmente, recordemos que pudimos llamar bar
antes lo definimos Esto se debe a la Segunda Regla del Levantamiento: Cuando el compilador de JavaScript encuentra una declaración de función, hace que su nombre y Definición disponible en la parte superior de su alcance. Reescribiendo nuestro código una vez más:
barra de funciones () si (! foo) alerta (foo + "? Esto es extraño ..."); var foo = "barra"; var quebrado; // barra indefinida (); // la barra ya está definida, ejecuta fine broken (); // ¡No se puede ejecutar indefinido! broken = function () alert ("¡Esta alerta no se mostrará!");
Una vez más, tiene mucho más sentido cuando escribir como JavaScript lee, no piensas?
Para revisar:
indefinido
hasta la asignación.Ahora echemos un vistazo a dos nuevas herramientas que funcionan de manera un poco diferente: dejar
y const
.
dejar
, const
, y la zona muerta temporal diferente a var
Declaraciones, variables declaradas con. dejar
y const
no hacer ser levantado por el compilador.
Al menos, no exactamente.
Recuerda como pudimos llamar roto
, pero consiguió un Error de tecleado
porque tratamos de ejecutar indefinido
? Si hubiéramos definido roto
con dejar
, habríamos conseguido un Error de referencia
, en lugar:
"uso estricto"; // Tienes que "usar estricto" para probar esto en Node broken (); // ReferenceError! let broken = function () alert ("¡Esta alerta no se mostrará!");
Cuando el compilador de JavaScript registra variables en sus ámbitos en su primer paso, trata dejar
y const
diferente a como lo hace var
.
Cuando encuentra un var
declaración, registramos el nombre de la variable a su alcance e inmediatamente inicializamos su valor para indefinido
.
Con dejar
, sin embargo, el compilador hace Registrar la variable a su alcance, pero noinicializar su valor para indefinido
. En cambio, deja la variable sin inicializar., hasta El motor ejecuta su declaración de asignación. Accediendo al valor de una variable no inicializada se lanza un Error de referencia
, lo que explica por qué el fragmento anterior se lanza cuando lo ejecutamos.
El espacio entre el principio de la parte superior del alcance de un dejar
declaración y la declaración de asignación se llama el Zona muerta temporal. El nombre proviene del hecho de que, a pesar de que el motor sabe sobre una variable llamada foo
en la parte superior del alcance de bar
, La variable está "muerta", porque no tiene un valor..
… También porque matará tu programa si intentas usarlo temprano.
los const
palabra clave funciona de la misma manera que dejar
, con dos diferencias clave:
const
.const
.Esto garantiza que const
será siempretiene el valor que inicialmente le asignó.
// Esto es legal const React = require ('reaccion'); // Esto no es totalmente legal const crypto; crypto = require ('crypto');
dejar
y const
son diferentes de var
De otra manera: el tamaño de sus alcances..
Cuando declara una variable con var
, es visible tan alto en la cadena de alcance como sea posible, normalmente, en la parte superior de la declaración de la función más cercana, o en el alcance global, si lo declara en el nivel superior.
Cuando declara una variable con dejar
o const
, sin embargo, es visible como en la zona como sea posible-solamente dentro del bloque mas cercano.
UNA bloquear es una sección de código dispuesta por llaves, como se ve con Si
/más
bloques, para
los bucles, y en fragmentos de código explícitamente "bloqueados", como en este fragmento.
"uso estricto"; vamos foo = "foo"; if (foo) const bar = "bar"; var foobar = foo + bar; console.log ("Puedo ver" + bar + "en este bloque."); prueba console.log ("Puedo ver" + foo + "en este bloque, pero no" + barra + "."); catch (err) console.log ("Obtuvo un" + err + "."); prueba console.log (foo + bar); // Se lanza debido a 'foo', pero ambos están indefinidos catch (err) console.log ("Acaba de obtener un" + err + "."); console.log (foobar); // Funciona bien
Si declara una variable con const
o dejar
dentro de un bloque, es solamente visible dentro del bloque, y solamente después de que lo hayas asignado.
Una variable declarada con var
, sin embargo, es visible tan lejos como sea posible-En este caso, en el ámbito global..
Si te interesan los detalles esenciales de dejar
y const
, vea lo que el Dr. Rauschmayer tiene que decir sobre ellos en Explorando ES6: Variables y alcance, y eche un vistazo a la documentación de MDN sobre ellos.
esta
Y funciones de flechaEn la superficie, esta
No parece tener mucho que ver con el alcance. Y, de hecho, JavaScript lo hace no resolver el significado de esta
De acuerdo con las reglas de alcance que hemos hablado aquí..
Al menos, no por lo general. JavaScript, notoriamente, hace no resolver el significado de la esta
palabra clave basada en donde la usaste:
var foo = nombre: 'Foo', idiomas: ['Español', 'Francés', 'Italiano'], habla: function speak () this.languages.forEach (function (language) console.log (this. nombre + "habla" + idioma + ".");); foo.speak ();
La mayoría de nosotros esperaríamos esta
significar foo
dentro de para cada
bucle, porque eso es lo que significa justo fuera de ella. En otras palabras, esperaríamos que JavaScript resolviera el significado de esta
léxicamente.
Pero no lo hace.
En su lugar, crea un nuevo esta
Dentro de cada función que definas, y decide qué significa en función de cómo llamas a la función -no dónde lo definiste.
Ese primer punto es similar al caso de redefinición. alguna Variable en un ámbito hijo:
function foo () var bar = "bar"; function baz () // La reutilización de nombres de variables como esta se denomina "shadowing" var bar = "BAR"; console.log (bar); // BAR baz (); foo (); // BAR
Reemplazar bar
con esta
, y todo debería aclararse al instante!
Tradicionalmente, conseguir esta
para trabajar como esperamos que funcionen las antiguas variables de ámbito léxico, se requiere una de estas dos soluciones:
var foo = nombre: 'Foo', idiomas: ['Español', 'Francés', 'Italiano'], speak_self: function speak_s () var self = this; self.languages.forEach (function (language) console.log (self.name + "habla" + language + ".");), speak_bound: function speak_b () this.languages.forEach (function (language ) console.log (this.name + "habla" + language + "."); .bind (foo)); // Más comúnmente: .bind (este); ;
En hablar a sí mismo
, salvamos el significado de esta
a la variable yo
, y use ese Variable para obtener la referencia que queremos. En speak_bound
, usamos enlazar
a permanentemente punto esta
a un objeto dado.
ES2015 nos trae una nueva alternativa: funciones de flecha..
A diferencia de las funciones "normales", las funciones de flecha hacen no sombra su alcance de los padres esta
valor mediante el establecimiento de su propia cuenta. Más bien, resuelven su significado. léxicamente.
En otras palabras, si usas esta
En una función de flecha, JavaScript busca su valor como lo haría con cualquier otra variable..
En primer lugar, comprueba el ámbito local para una esta
valor. Como las funciones de flecha no configuran una, no la encontrará. A continuación, comprueba la padre alcance para un esta
valor. Si encuentra uno, usará eso, en vez.
Esto nos permite reescribir el código anterior de esta manera:
var foo = nombre: 'Foo', idiomas: ['Spanish', 'French', 'Italian'], speak: function speak () this.languages.forEach ((language) => console.log (this .name + "habla" + language + "."););
Si desea más detalles sobre las funciones de flecha, eche un vistazo al excelente curso de Dan Wellman, Instructor de Envato Tuts + sobre Fundamentos de JavaScript ES6, así como a la documentación de MDN sobre funciones de flecha.
Hemos cubierto mucho terreno hasta ahora! En este artículo, has aprendido que:
dejar
o const
antes de la asignación lanza un Error de referencia
, y que tales variables estén orientadas al bloque más cercano..esta
, y eludir el enlace dinámico tradicional.También has visto las dos reglas de izar:
var
Las declaraciones están disponibles en todos los ámbitos en los que están definidos, pero tienen el valor indefinido
hasta que sus instrucciones de asignación se ejecuten.Un buen próximo paso es utilizar su nuevo conocimiento de los ámbitos de JavaScript para envolver su cabeza en torno a los cierres. Para eso, echa un vistazo a los alcances y cierres de Kyle Simpson.
Por último, hay mucho más que decir sobre esta
de lo que pude cubrir aquí. Si la palabra clave todavía parece mucha magia negra, eche un vistazo a esto y haga clic en Prototipos de objetos para entenderlo..
Mientras tanto, toma lo que has aprendido y escribe menos errores.!
Aprender JavaScript: la guía completa
Hemos creado una guía completa para ayudarlo a aprender JavaScript, ya sea que esté comenzando a trabajar como desarrollador web o si desea explorar temas más avanzados..