Var, let o const? Problemas de alcance variable y ES6

Los ámbitos en JavaScript siempre han sido un tema complicado, especialmente en comparación con lenguajes más estrictamente organizados como C y Java. Durante muchos años, el alcance en JS no fue particularmente discutido, ya que el lenguaje simplemente no tenía ningún medio que afectara significativamente la situación actual. Pero en ECMAScript 6 hay algunas características nuevas que permiten a los desarrolladores controlar mejor el alcance de las variables. Estas características ahora son muy compatibles con los navegadores, son bastante accesibles para la mayoría de los desarrolladores. Sin embargo, las nuevas palabras clave para declarar variables, teniendo en cuenta el hecho de que la antigua palabra clave var no ha desaparecido, significan no solo nuevas oportunidades, sino también la aparición de nuevas preguntas. ¿Cuándo usar las palabras clave let y const ? ¿Cómo se comportan? ¿En qué situaciones la palabra clave var sigue siendo relevante? El material, cuya traducción publicamos hoy, tiene como objetivo explorar el problema del alcance de las variables en JavaScript.



Ámbitos variables: una visión general


El alcance de una variable es un concepto importante en la programación, que, sin embargo, puede confundir a algunos desarrolladores, especialmente a los novatos. El alcance de una variable es la parte del programa en la que se puede acceder a esta variable.

Eche un vistazo al siguiente ejemplo:

 var myVar = 1; function setMyVar() { myVar = 2; } setMyVar(); console.log(myVar); 

¿Cuál será el resultado del método console.log ? La respuesta a esta pregunta no sorprenderá a nadie: generará 2 . La variable myVar declara fuera de una función, lo que nos dice que se declara en el ámbito global. Por lo tanto, cualquier función declarada en el mismo alcance podrá acceder a myVar . De hecho, cuando se trata de código ejecutado en un navegador, incluso las funciones declaradas en otros archivos conectados a la página tendrán acceso a esta variable.

Ahora eche un vistazo al siguiente código:

 function setMyVar() { var myVar = 2; } setMyVar(); console.log(myVar); 

Exteriormente, sus cambios, en comparación con el ejemplo anterior, son insignificantes. Es decir, simplemente colocamos la declaración de variable dentro de la función. ¿Cuál será la salida de console.log ahora? De hecho, nada, ya que esta variable no se declara y cuando intenta acceder a ella, se mostrará un mensaje sobre un error ReferenceError no manejado. Sucedió porque la variable se declaró dentro de la función utilizando la palabra clave var . Como resultado, el alcance de esta variable se limita al alcance interno de la función. Se puede acceder en el cuerpo de esta función, las funciones integradas en esta función pueden funcionar con ella, pero no es accesible desde el exterior. Si necesitamos varias funciones en el mismo nivel para usar una determinada variable, debemos declarar esta variable en el mismo lugar donde se declaran estas funciones, es decir, un nivel más alto que su alcance interno.

Aquí hay una observación interesante: el código de la mayoría de los sitios web y aplicaciones web no se aplica al trabajo de ningún programador. La mayoría de los proyectos de software son el resultado del desarrollo del equipo y, además, utilizan bibliotecas y marcos de terceros. Incluso si solo un programador está involucrado en el desarrollo de un sitio web, generalmente utiliza recursos externos. Debido a esto, generalmente no se recomienda declarar variables en el ámbito global, ya que no puede saber de antemano qué variables serán declaradas por otros desarrolladores cuyo código se utilizará en el proyecto. Para solucionar este problema, puede usar algunos trucos, en particular, el patrón " Módulo " y IIFE al aplicar el enfoque orientado a objetos para el desarrollo de JavaScript, aunque la encapsulación de datos y funciones en objetos comunes puede lograr el mismo efecto. En general, se puede observar que las variables cuyo alcance va más allá de lo que necesitan suelen ser un problema con el que hay que hacer algo.

Problema de palabra clave var


Entonces, descubrimos el concepto de "alcance". Ahora pasemos a cosas más complejas. Echa un vistazo al siguiente código:

 function varTest() { for (var i = 0; i < 3; i++) {   console.log(i); } console.log(i); } varTest(); 

¿Qué llegará a la consola después de su ejecución? Está claro que los valores del contador creciente i : 0 , 1 y 2 se mostrarán dentro del bucle. Una vez que finaliza el ciclo, el programa continúa ejecutándose. Ahora estamos intentando acceder a la misma variable de contador que se declaró en el bucle for , fuera de este bucle. ¿Qué saldrá de esto?

Después de llamar a i fuera del ciclo, 3 ingresará a la consola, ya que la palabra clave var actúa en el nivel de función. Si declara una variable usando var , puede acceder a ella en una función incluso después de salir de la construcción donde se declaró.

Esto puede convertirse en un problema cuando las funciones se vuelven más complejas. Considere el siguiente ejemplo:

 function doSomething() { var myVar = 1; if (true) {   var myVar = 2;   console.log(myVar); } console.log(myVar); } doSomething(); 

¿Qué llegará a la consola ahora? 2 y 2 . Declaramos una variable, la inicializamos con el número 1 y luego intentamos redefinir la misma variable dentro de la if . Dado que estas dos declaraciones existen en el mismo ámbito, no podemos declarar una nueva variable con el mismo nombre, aunque obviamente queremos hacer exactamente eso. Como resultado, la primera variable se sobrescribe dentro de la if .

Este es precisamente el mayor defecto de la palabra clave var . El alcance de las variables declaradas al usarlo es demasiado grande. Esto puede conducir a la sobrescritura involuntaria de datos y otros errores. Grandes áreas de visibilidad a menudo conducen a programas inexactos. En general, una variable debe tener un alcance limitado por sus necesidades, pero sin excederlas. Sería bueno poder declarar variables cuyo alcance no sea tan grande como cuando se usa var , lo que haría posible, si es necesario, usar construcciones de software más estables y mejores a prueba de errores. En realidad, ECMAScript 6 nos brinda tales oportunidades.

Nuevas formas de declarar variables


El estándar ECMAScript 6 (un nuevo conjunto de características de JavaScript, también conocido como ES6 y ES2015) nos ofrece dos nuevas formas de declarar variables que difieren en alcance, en comparación con var , y tienen algunas características más. Estas son las palabras clave let y const . Ambos nos dan el llamado alcance de bloque. Esto significa que el alcance de su uso puede limitarse a un bloque de código, como un bucle for o una if . Esto le da al desarrollador más flexibilidad para elegir el alcance de las variables. Considere las nuevas palabras clave.

▍Uso de la palabra clave let


La palabra clave let es muy similar a var , la principal diferencia es el alcance limitado de las variables declaradas con ella. Reescribimos uno de los ejemplos anteriores, reemplazando var con let :

 function doSomething() { let myVar = 1; if (true) {   let myVar = 2;   console.log(myVar); } console.log(myVar); } doSomething(); 

En este caso, los números 2 y 1 llegarán a la consola. Esto sucede porque la if establece un nuevo alcance para la variable declarada con la palabra clave let . Esto lleva al hecho de que la segunda variable declarada es una entidad completamente independiente, no relacionada con la primera. Puede trabajar con ellos independientemente uno del otro. Sin embargo, esto no significa que los bloques de código anidados, como nuestra if , estén completamente separados de las variables declaradas con la palabra clave let en el ámbito en el que se encuentran. Echa un vistazo al siguiente código:

 function doSomething() { let myVar = 1; if (true) {   console.log(myVar); } } doSomething(); 

En este ejemplo, la consola obtendrá el número 1 . El código dentro de la if tiene acceso a la variable que creamos fuera de ella. Por lo tanto, muestra su valor en la consola. ¿Y qué pasa si intentas mezclar el alcance? Por ejemplo, haz esto:

 function doSomething() { let myVar = 1; if (true) {   console.log(myVar);   let myVar = 2;   console.log(myVar); } } doSomething(); 

Puede parecer que la primera llamada console.log generará 1 , pero de hecho, cuando intente ejecutar este código, aparecerá un error de ReferenceError que nos indica que la variable myVar para este alcance no está definida o no está inicializada (el texto de este error difiere en diferentes navegadores) En JavaScript, existe la posibilidad de elevar las variables a la parte superior de su alcance. Es decir, si una variable se declara en un determinado ámbito, JavaScript le reserva un lugar incluso antes de que se ejecute el comando para declararla. Cómo exactamente esto sucede difiere cuando se usa var y let .

Considere el siguiente ejemplo:

 console.log(varTest); var varTest = 1; console.log(letTest); let letTest = 2; 

En ambos casos, intentamos usar la variable antes de declararla. Pero los comandos de salida de la consola se comportan de manera diferente. El primero, usar una variable que luego se declarará usando la palabra clave var , generará un resultado undefined , es decir, lo que se escribirá en esta variable. El segundo comando, que intenta acceder a la variable, que luego se declarará utilizando la palabra clave let , arrojará un ReferenceError y nos dirá que estamos tratando de usar la variable antes de declararla o inicializarla. Que pasa

Pero el hecho es que antes de que se ejecute el código, los mecanismos responsables de su ejecución observan este código, descubren si se declararán variables en él y, de ser así, los plantean con reserva de espacio para ellos. En este caso, las variables declaradas usando la palabra clave var se inicializan a undefined dentro de su alcance, incluso si se accede a ellas antes de declararlas. El problema principal aquí es que el valor undefined en una variable no siempre indica que están tratando de usar la variable antes de su declaración. Eche un vistazo al siguiente ejemplo:

 var var1; console.log(var1); console.log(var2); var var2 = 1; 

En este caso, aunque var1 y var2 declaran de manera diferente, ambas llamadas a console.log saldrán undefined . El punto aquí es que en las variables declaradas con var , pero no inicializadas, el valor undefined escribe automáticamente. Al mismo tiempo, como ya dijimos, las variables declaradas usando var , a las que se accede antes de declararse, también contienen undefined . Como resultado, si algo sale mal en dicho código, no será posible entender cuál es exactamente la fuente del error: usar una variable no inicializada o usar una variable antes de su declaración.

El lugar para las variables declaradas con la palabra clave let está reservado en su bloque, pero, antes de ser declaradas, caen en la zona muerta temporal (TDZ, Zona muerta temporal). Esto lleva al hecho de que no se pueden usar antes de que se declaren, y un intento de acceder a dicha variable conduce a un error. Sin embargo, el sistema conoce exactamente la causa del problema y lo informa. Esto se ve claramente en este ejemplo:

 let var1; console.log(var1); console.log(var2); let var2 = 1; 

Aquí, la primera llamada a console.log saldrá undefined , y la segunda arrojará un error ReferenceError , diciéndonos que la variable aún no ha sido declarada o inicializada.

Como resultado, si el uso de var aparece undefined , no sabemos la razón de este comportamiento del programa. Una variable puede declararse y no inicializarse, o puede que aún no se declare en este ámbito, pero se declarará en el código ubicado debajo del comando para acceder a ella. Usando la palabra clave let , podemos entender qué está sucediendo exactamente, lo cual es mucho más útil para la depuración.

▍Uso de la palabra clave const


La palabra clave const es muy similar a let , pero tienen una diferencia importante. Esta palabra clave se usa para declarar constantes. Los valores de las constantes no se pueden cambiar después de su inicialización. Cabe señalar que esto se aplica solo a valores de tipos primitivos, agua, cadenas o números. Si la constante es algo más complejo, por ejemplo, un objeto o una matriz, la estructura interna de dicha entidad puede modificarse, no puede simplemente reemplazarla por otra. Echa un vistazo al siguiente código:

 let mutableVar = 1; const immutableVar = 2; mutableVar = 3; immutableVar = 4; 

Este código se ejecutará hasta la última línea. Intentar asignar un nuevo valor a una constante dará como resultado un error TypeError . Así es como se comportan las constantes, pero, como ya se mencionó, los objetos que las constantes se inicializan pueden cambiar, pueden sufrir mutaciones, lo que puede llevar a sorpresas .

Quizás usted, como desarrollador de JavaScript, se pregunte por qué es importante la inmunidad de las variables. Las constantes son un fenómeno nuevo en JavaScript, mientras que son una parte esencial de lenguajes como C o Java. ¿Por qué es tan popular este concepto? El hecho es que el uso de constantes nos hace pensar en cómo funciona nuestro código. En algunas situaciones, cambiar el valor de una variable puede alterar el código, por ejemplo, si se escribe el número Pi y se accede a él constantemente, o si la variable tiene un enlace a un elemento HTML con el que necesita trabajar constantemente. Digamos, aquí hay una constante en la que se escribe un enlace a un botón determinado:

 const myButton = document.querySelector('#my-button'); 

Si el código depende del enlace al elemento HTML, entonces debemos asegurarnos de la inmutabilidad de este enlace. Como resultado, podemos decir que la palabra clave const no solo va por el camino de las mejoras en el campo de visibilidad, sino también por el camino de limitar la posibilidad de modificar los valores de las constantes declaradas usando esta palabra clave. Recuerde cómo dijimos que una variable debería tener exactamente el alcance que necesita. Esta idea puede continuar presentando una recomendación, según la cual una variable solo debe tener la capacidad de cambiar, lo cual es necesario para un trabajo adecuado con ella, y nada más. Aquí hay un buen material sobre el tema de la inmunidad, del cual se puede sacar una conclusión importante, según la cual el uso de variables inmutables nos hace pensar más cuidadosamente sobre nuestro código, lo que conduce a una mejora en la pureza del código y a una reducción en el número de sorpresas desagradables que surgen de su funcionamiento.

Cuando comencé a usar las palabras clave let y const , básicamente usé let , recurriendo a const solo cuando escribir un nuevo valor en una variable declarada con let podría dañar el programa. Pero, aprendiendo más sobre programación, cambié de opinión al respecto. Ahora mi herramienta principal es const , y me let usarla solo cuando el valor de la variable necesita ser reescrito. Esto me hace pensar si es realmente necesario cambiar el valor de una determinada variable. En la mayoría de los casos, esto no es necesario.

¿Necesitamos la palabra clave var?


Las palabras clave let y const contribuyen a un enfoque de programación más responsable. ¿Hay situaciones en las que todavía se necesita la palabra clave var ? Si los hay. Hay varias situaciones en las que esta palabra clave sigue siendo útil para nosotros. Considere cuidadosamente de qué hablaremos antes de cambiar var para let o const .

▍ Nivel de soporte de palabras clave Var por navegadores


Las variables declaradas con la palabra clave var tienen una característica muy importante que let y const falta. Es decir, estamos hablando del hecho de que absolutamente todos los navegadores admiten esta palabra clave. Aunque el soporte para let y const por parte de los navegadores es muy bueno, existe el riesgo de que su programa termine en un navegador que no los admite. Para comprender las consecuencias de tal incidente, debe considerar cómo los navegadores manejan el código JavaScript no compatible, en lugar de, por ejemplo, cómo reaccionan al código CSS que no entienden.

Si el navegador no admite alguna función de CSS, esto básicamente conduce a cierta distorsión de lo que se mostrará en la pantalla. Un sitio en un navegador que no admite ninguno de los estilos utilizados por el sitio no se verá como se esperaba, pero es muy probable que se pueda usar. Si usa, por ejemplo, let , y el navegador no admite esta palabra clave, entonces su código JS simplemente no funcionará allí. No será, eso es todo. Dado que JavaScript es uno de los componentes importantes de la web moderna, esto puede convertirse en un problema grave si necesita que sus programas funcionen en navegadores obsoletos.

Cuando las personas hablan sobre la compatibilidad del navegador para los sitios, generalmente preguntan en qué navegador funcionará el sitio de manera óptima. Si estamos hablando de un sitio cuya funcionalidad se basa en el uso de let y const , entonces una pregunta similar tendrá que plantearse de manera diferente: "¿En qué navegadores no funcionará nuestro sitio?". Y esto es mucho más serio que hablar sobre si usar display: flex o no. Para la mayoría de los sitios web, la cantidad de usuarios con navegadores obsoletos no será lo suficientemente grande como para preocuparse. Sin embargo, si estamos hablando de algo como una tienda en línea o sitios cuyos propietarios compran publicidad, esto puede ser una consideración muy importante. Antes de utilizar nuevas oportunidades en tales proyectos, evalúe el nivel de riesgo.

Si necesita admitir navegadores realmente antiguos, pero desea utilizar let , const y otras características nuevas de ES6, una de las soluciones a este problema es utilizar un transportador de JavaScript como Babel . Los transpiladores proporcionan la traducción del nuevo código a lo que los navegadores antiguos entenderán. Con Babel, puede escribir código moderno que use las últimas características del lenguaje y luego convertirlo en código que puedan ejecutar los navegadores más antiguos.

, ? , . , , , , . , . , , . ES6-, Babel, Babel , , . , , . . ? - IE8 ? , , , , , .

▍ var


, var , . . :

 var myVar = 1; function myFunction() { var myVar = 2; //  ,     myVar    ! } 

, myVar , , . , . , , , , . , . var .

 var myVar = 1; function myFunction() { var myVar = 2; console.log(myVar); // 2 console.log(window.myVar); // 1 } 

var , window . let const . , JS- , (, , ) , .

, . , . , , , :

 let myGlobalVars = {}; let myVar = 1; myGlobalVars.myVar = myVar; function myFunction() { let myVar = 2; console.log(myVar); // 2 console.log(myGlobalVars.myVar); // 1 } 

, , , , . , , var , , , , , .

Resumen


, ? ? :

  • IE10 - ? — var .
  • JavaScript, , , var , const . - (, , ) — let .

let const , ECMAScript 6, ( ) - -. , , , . , - , «» «» , , let const , .

! , const var , let , , , ?

Source: https://habr.com/ru/post/es420359/


All Articles