JavaScript — Scoping — scope, contexto de ejecución, var, let, const, y más…
Temario
- Introducción (scope, contexto de ejecución — global y local)
- var
- Let
- const
- Ventajas de usar let y const
- Block-Scoped Functions
- Buenas prácticas (let, const)
i. Introducción
Primero, ¿qué es scope?
El contexto actual de ejecución. El contexto en el que los valores y las expresiones son “visibles” o pueden ser referenciados… [ref]
De manera un poco más simple: Es el alcance (visibilidad) que vamos a tener de una variable para poder utilizarla.
En JavaScript el scope lo podemos dividir de la siguiente manera:
- Global (se puede acceder desde cualquier parte de nuestro código)
- Local (su acceso es limitado) donde a su vez se divide en dos: por función y por bloque
A estos conceptos también se les conoce como contexto de ejecución, ya que cada vez que JavaScript lee un código, siempre evalúa su entorno y ejecuta el código.
Bien ya tenemos las definiciones, ahora vamos a explicar con un ejemplo como funciona cada una…
Imaginemos que somos un guerrero y tenemos diferentes armas/poderes, donde podemos hacer uso (acceder) a ellas según donde nos encontremos:
Sword — Global scope
Para global scope su visibilidad y alcance es global, siguiendo nuestro ejemplo… quiere decir que nuestra espada la podemos ocupar en cualquier parte (en el campo, en los pasillos de un castillo o en algún cuarto).
Fire — Function scope
Para function scope su visibilidad y alcance es un poco limitada, siguiendo nuestro ejemplo….El Fuego lo podemos ocupar en los pasillos del castillo, así como en algún cuarto, pero NO en el campo.
Wizard — Block scope
Para block scope su visibilidad y alcance es aún más limitada que function scope, siguiendo nuestro ejemplo…El Mago lo podemos ocupar en algún cuarto, pero NO en el pasillo del castillo, así como tampoco en el campo.
Teniendo en cuenta los conceptos y el ejemplo, ha llegado el momento de ver como funciona var, let y const…
ii. Var
var es utilizada para declarar una variable, ya que sea que se inicialice al momento o después.
Ejemplo:
Recordando los conceptos que vimos (global y local scope), identifiquemos el alcance que tiene la variable tipo var:
Global Scope: Podemos definir variables tipo var donde su alcance sea global y se pueda acceder desde cualquier lugar…
Ejemplo:
Function Scope: Podemos definir una variable tipo var, donde su alcance sea dentro de la función y solo se pueda acceder a ella dentro de la misma…
Ejemplo:
Si nosotros intentamos acceder a la variable exampleFunctionScope fuera de la función nos arrojaría un error ya que no existe y además rompería el código:
Block Scope: Para el caso de la variable tipo var, el block scope su alcance es el mismo que function scope, esto se debe a que son procesadas antes de la ejecución del código (ver el post de hoisting[ref] donde se explica a fondo)…
Ejemplo:
Hemos definido la variable dentro del if (room), donde la consola nos regresa el valor esperado “ Im the room and the value of exampleBlockScope is: 100”… como mencionamos anteriormente su alcance es function scope, veamos el siguiente ejemplo para entender…
Hemos agregado dos consolas, una antes de la definición de la variable, y otra después de la definición; si observamos:
- La primera consola (antes de su definición) la variable exampleBlockScope nos retorna undefined y no un error.
- La segunda consola (dentro del if) ya tiene un valor la variable exampleBlockScope y por eso nos regresa el valor de 100
- En la tercera consola (después del if) ya tiene el valor de 100 ya que su alcance al ser function scope no nos arroja error.
Entonces, en resumen, el alcance que tiene una variable tipo var SIEMPRE va a ser scope function.
Las ventajas de usar var :
- Las variables declaradas con var son procesadas antes de la ejecución del código (ver hoisting, donde se explica más a fondo [ref]).
- Su scope es su contexto de ejecución (entorno en el que se está en ejecución el código) (ver hoisting, donde se explica más a fondo [ref]).
- El scope declarado fuera de una función es global.
Veamos un ejemplo un poco mas avanzado:
El ejemplo anterior, visualmente debe verse así:
Entonces de acuerdo su alcance, ¿qué resultado crees que va a salir en cada uno?…
Respuesta:
Pero ahora que pasaría si tenemos la misma variable declarada en diferentes ámbitos:
Tenemos declarada 3 variables:
- Global var a = 5;
- Local function var a = 10;
- Local bloque var a = 20;
Recordemos que la variable tipo var su alcance es function scope, entonces visualmente debe verse algo así:
Entonces de acuerdo su alcance, ¿qué resultado crees que va a salir en cada uno?…
Respuesta:
Recordemos que JavaScript es un lenguaje de alcance léxico, esto quiere decir que la herencia es de afuera hacia adentro; es importante recordarlo ya que las variables que están fuera de una función pueden ocuparse tanto dentro como fuera de ella, pero no al revés (una variable dentro de una función no la podemos ocupar fuera).
iii. Let
Se dice que let es el nuevo var, pero su scope está definido por el ámbito de bloque (block-scoped), esto quiere decir que el alcance de la variable estará definido dentro de un bloque de código.
Veamos cómo funciona su alcance:
Su Global Scope y Function Scope funciona CASI igual que var… la GRAN diferencia, es la declaración que está dentro del ámbito Lexical-Enviroment (ver el post de hoisting[ref] donde se explica a fondo), esto quiere decir que su alcance solo será en el bloque que se ha creado y al momento de ser ejecutada.
Global Scope: Podemos definir variables tipo let donde su alcance sea global y se pueda acceder desde cualquier lugar…
Ejemplo:
Function Scope: Podemos definir una variable tipo let, donde su alcance sea dentro de la función (al final la función es un bloque) y solo se pueda acceder a ella dentro de la misma…
Ejemplo:
Si nosotros intentamos acceder a la variable exampleFunctionScope fuera de la función nos arrojaría un error y rompería el código:
Block Scope: Podemos definir una variable tipo let, donde su alcance sea interno (bloque) y solo se pueda acceder a ella dentro de la misma…
Ejemplo:
Hemos definido la variable dentro del if (room), donde la consola nos regresa el valor esperado “ Im the room and the value of exampleBlockScope is: 100”… como mencionamos anteriormente, su alcance es block scope (if, for, etc), veamos qué pasa si agregamos consolas fuera del block scope…
Automáticamente nos va a salir un error y va a romper el código, ya que la variable no existe fuera del bloque de código.
Veamos los ejemplos que usamos con var, pero ahora con let:
El ejemplo anterior, visualmente debe verse así:
Entonces de acuerdo su alcance, ¿qué resultado crees que va a salir en cada uno?…
Respuesta:
Pero ahora que pasaría si tenemos la misma variable declarada en diferentes ámbitos:
Tenemos declarada 3 variables:
- Global let a = 5;
- Local función let a = 10;
- Local bloque let a = 20;
Recordemos que la variable tipo let su alcance es block scope, entonces visualmente debe verse algo así:
Podemos observar en la imagen que efectivamente está declarada 3 veces la variable a diferencia de var (solo 2)…Entonces de acuerdo su alcance, ¿qué resultado crees que va a salir en cada uno?…
Respuesta:
iv. const
const tiene la misma funcionalidad y alcance que tienen let solo que no nos permite re-asignar su valor (primitive).
Si intentamos cambiar el valor de una const en consola nos va a mostrar un error, ya que la variable es constante y no puede cambiarse su valor.
Mencionamos que solo afecta a los primitivos, ya que los objetos SI podemos cambiar sus valores (hijos), e inclusive agregar más propiedades; veamos el siguiente ejemplo:
Podemos observar en el ejemplo anterior que declaramos a.friend como Mauricio, después le hemos cambiado su valor a Benjamin e inclusive pudimos agregar una nueva propiedad (a.otherFriend = ‘David’).
Entonces const = inmutabilidad, si bien las variables no se pueden reasignar, como tal NO las hace realmente inmutables, ya que como hemos visto con el ejemplo anterior el contenido de los objetos podemos cambiarlos sin ningún problema.
La única forma que nos puede salir error es si la variable a cambiamos el tipo de dato:
v. Ventajas de usar let y const
Una de las ventajas de usar let y const es en la declaración de las variables, veamos primero un ejemplo con var
Este es un error muy común en los desarrolladores y más cuando varias personas trabajan en la misma aplicación, un desarrollador ha declarado a = 100, y después de mucho código llega otro desarrollador y declara la misma variable pero con otro valor a =’Hola’, y si ese valor más abajo se ocupa (antes se esperaba un number, pero ahora va a regresar un string), y en producción detectar estos errores es muy complicado, por lo que de una vez más let y const vienen a rescatarnos…
Veamos el mismo ejemplo, pero ahora con let:
Si se llega a declarar la misma variable dentro de un mismo block-scope, la consola nos va a mostrar un error, ya que la variable a ya fue declarada antes.
No olvidemos que let y const su scope está definido por el ámbito de bloque block-scoped.
Veamos el siguiente ejemplo:
Como mencionamos anteriormente let y const su ámbito es block-scoped mientras que var es su contexto de ejecución.
En el ejemplo anterior tenemos declarada dos veces la variable a, pero en diferentes ámbitos, veamos otro ejemplo:
En el ejemplo de arriba, la variable i que está definida con let solo existe dentro del ciclo for, por lo que la consola que está dentro de la misma si imprime el valor de i, mientras que la consola que está fuera imprime un error ya que la variable i no está definida.
vi. Block-Scoped Functions
Al igual que let y const, las funciones adoptan el estándar block scoped, en ES5 teníamos que emular el scope de la función con una IIFE[ref].
Como podemos observar con ES6 es más legible y simple el código, ya que solo es necesario que encerremos nuestras funciones dentro del block { }
vii. Buenas prácticas
Utilizar let o const para cada una de las variables declaradas ya que tiene sus ventajas:
- Más fácil agregar nuevas variables.
- No hay que estar cambiando el ; por , o viceversa
- Cuando usamos el debugger es más fácil ir una a una.
Se recomienda agrupar primero los const y luego let; ya que es más fácil de leer y además facilita la reasignación de valores.
No hay que encadenar variables, ya que JavaScript las asigna de manera implícita como globales.
Si vas a declarar variables ÚSALAS
No OLVIDEMOS que let y const están definidas por block-scoped
En la siguiente entrega vamos a ver JavaScript — Hoisting.
La entrega pasada vimos JavaScript — Funciones
Bibliografía y links que te puede interesar…