Lexical scope es un concepto algo intimidante la primera vez que lo escuchas. Traducido del inglés sería algo como “Alcance léxico” y no se tú pero a mi ese nombrecito por si solo no me dice gran cosa. Recuerdo que para mi fue de esos conceptos que tuve que hacer un esfuerzo consciente para poder aprender, no fue algo que viniera naturalmente a mi mente como el concepto de una pokébola o un kamehameha.
Sin embargo en realidad no es algo tan complejo. Y de hecho el nombre si tiene sentido, pero sólo hasta que aprendes acerca de temas como analizadores léxicos, y eso… esta fuera del scope de este post (lol).
Básicamente cuando tu estas programando y creas una variable o función, ese elemento no lo puedes leer desde cualquier lado. El Lexical Scope hace referencia precisamente al alcance que tiene tu elemento, es decir, desde que tantos lugares se puede acceder a él. Habrá variables y funciones con un tremendo lexical scope que te caes de tan grandote que está y habrá otras con uno pues mas humilde y modestillo.
Y como muchas cosas en la vida, el lexical scope también lo clasificamos de acuerdo a su tamaño:
- global scope – Tiene alcance a nivel pues..global. A lo largo de todo el programa
- local scope – Tiene alcance solo dentro de una función específica.
- block scope – Tiene alcance solo dentro de una sección/bloque de código: for, while, if, else
Podríamos dejar la explicación ahí, pero pues ver unos dibujitos con un ejemplos nunca está de mas.
Imaginate un parque de diversiones. Un parque normal, con algunas atracciones y con puestos de comida. Nada del otro mundo.
Como todo buen miembro de la sociedad compras tu boleto (uno de los mejors boletos diseñados para un parque) y te dispones a entrar
Global Scope
El Global Scope es el lugar donde típicamente Javascript junta todos los elementos declarados a lo largo de uno o varios archivos. En esencia es el lugar central donde se puede acceder a todas las referencias principales.
En un parque de atracciones, los visitantes por su supuesto necesitan su boleto para poder entrar. Cualquier empleado en cualquier atracción puede solicitarte el boleto pues para comprobar que efectivamente hayas comprado tu entrada.
El área verde es todo el global scope. Tu boleto tiene validez para cualquier elemento que se encuentre dentro del global scope.
Por ende cualquier elemento global es accesible desde cualquier lado:
let boleto = 'Tremendo Boleto';
function casaEmbrujada() {
//TODO: Cosas de Casa Embrujada
console.log('boleto llamado desde "casaEmbrujada" --- ', boleto)
}
function montaniaRusa() {
//TODO: Cosas de Montaña Rusa
console.log('boleto llamado desde "montaniaRusa" --- ', boleto)
}
function arcade () {
//TODO: Cosas de arcade
console.log('boleto llamado desde "arcade" --- ', boleto)
}
function restaurante() {
//TODO: Cosas de restaurante
console.log('boleto llamado desde "restaurante" --- ', boleto)
}
console.log('boleto llamado desde "global" --- ', boleto)
casaEmbrujada()
montaniaRusa()
arcade()
restaurante()
¿Qué te imaginas que va a pasar si corremos este programa? … Pues como boletoes accesible para todos, va imprimir 5 líneas diferentes.
boleto llamado desde "global" --- Tremendo Boleto
boleto llamado desde "casaEmbrujada" --- Tremendo Boleto
boleto llamado desde "montaniaRusa" --- Tremendo Boleto
boleto llamado desde "arcade" --- Tremendo Boleto
boleto llamado desde "restaurante" --- Tremendo Boleto
Local Scope
El Local Scope en contraste con el global, se define solo como el área que abarca una función.
Supón que en este parque se ofrece la posibilidad de comprar pases VIP para un par de lugares:
Lo que estos pases hacen es básicamente acumular puntos para futuras visitas dentro de estos 2 lugares.
Como te podrás imaginar, esto significa que el pase de VIP Arcade no sirve en otro lado que no sea el arcade y el pase VIP Restaurante no sirve en otro lado que no sea el restaurante.
Veamos un ejemplo de esta lógica en el código:
let boleto = 'BOLETO';
...
function arcade () {
let paseVIPArcade = 'VIP-Arcade'
console.log('paseVIPArcade llamado desde "arcade" --- ', paseVIPArcade)
}
function restaurante() {
let paseVIPRestaurante = 'VIP-Restaurante'
console.log('paseVIPRestaurante llamado desde "restaurante" --- ', paseVIPRestaurante)
}
console.log('boleto llamado desde "global" --- ', boleto)
console.log('paseVIPArcade llamado desde "global" --- ', paseVIPArcade)
console.log('paseVIPRestaurante llamado desde "global" --- ', paseVIPRestaurante)
arcade()
restaurante()
¿Qué crees que pase al intentar correr este ejemplo?
boleto llamado desde "global" --- BOLETO
...\src\lexicalScope\2-localScope\tempCodeRunnerFile.js:14
console.log('paseVIPArcade llamado desde "global" --- ', paseVIPArcade)
^
ReferenceError: paseVIPArcade is not defined
Bueno pues justo lo que dije. No se puede. El código falla porque el global scope no encuentra referencias a paseVIPArcade
.
Ese pase solo existe y es válido dentro de la función de arcade
. Y claro el error ahorita nos indica que el culpable es paseVIPArcade
porqué es el primero que encontró. Pero paseVIPRestaurante
está cometiendo la misma falta.
¿Y si quitamos las referencias de ambos pases en el global scope?
let boleto = 'BOLETO';
function arcade () {
let paseVIPArcade = 'VIP-Arcade'
console.log('paseVIPArcade llamado desde "arcade" --- ', paseVIPArcade)
}
function restaurante() {
let paseVIPRestaurante = 'VIP-Restaurante'
console.log('paseVIPRestaurante llamado desde "restaurante" --- ', paseVIPRestaurante)
}
console.log('boleto llamado desde "global" --- ', boleto)
arcade()
restaurante()
¡Ahora si! El código ya tiene sentido.
boleto llamado desde "global" --- BOLETO
paseVIPArcade llamado desde "arcade" --- VIP-Arcade
paseVIPRestaurante llamado desde "restaurante" --- VIP-Restaurante
Block Scope
El block scope es similar al local scope, sólo que en vez de referirse al área que abarca una función, se refiere al área de un bloque de código. Comúnmente un if, else,while, for
, etc
En el restaurante hay shows en ciertos horarios. Cualquiera puede atenderlos pero los que tienen un pase VIP tienen un bonus. Al incio de cada show se les da un ticket que sirve para rifar un premio al final del mismo.
El ticket no sirve para otros shows y porsupuesto mucho menos sirve fuera del restaurante.
let boleto = 'BOLETO';
function restaurante() {
let paseVIPRestaurante = 'VIP-Restaurante'
console.log('paseVIPRestaurante llamado desde "restaurante" --- ', paseVIPRestaurante)
console.log('tickets llamado desde "restaurante" --- ', tickets)
if (typeof paseVIPRestaurante === 'string') {
let tickets = 10
console.log('tickets llamado desde "restaurante" en la expresión if --- ', tickets)
}
}
console.log('boleto llamado desde "global" --- ', boleto)
console.log('tickets llamado desde "restaurante" --- ', tickets)
restaurante()
En primera instancia no te debe sorprender ver el mismo error a nivel global que se vió en el ejemplo de localScope.
boleto llamado desde "global" --- BOLETO
...\src\lexicalScope\3-blockScope\tempCodeRunnerFile.js:15
console.log('tickets llamado desde "global" --- ', tickets)
^
ReferenceError: tickets is not defined
Sin embargo corregir esa línea no es sufciente. Si tu borras la línea de console.log('tickets llamado desde "global" --- ', tickets)
obtendrás el mismo error, pero esta vez dentro de la función de restaurante():
boleto llamado desde "global" --- BOLETO
...\src\lexicalScope\3-blockScope\tempCodeRunnerFile.js:7
console.log('tickets llamado desde "restaurante" --- ', tickets)
^
ReferenceError: tickets is not defined
La razón ya la sabes, dado que tickets se declaró dentro de la expresión if, no podrás usarla fuera de ese bloque.
Por ende para que el código funcione tienes que quitar todas las referencias a tickets
fuera del block scope.
let boleto = 'BOLETO';
function restaurante() {
let paseVIPRestaurante = 'VIP-Restaurante'
console.log('paseVIPRestaurante llamado desde "restaurante" --- ', paseVIPRestaurante)
if (typeof paseVIPRestaurante === 'string') {
let tickets = 10
console.log('tickets llamado desde "restaurante" en la expresión if --- ', tickets)
}
}
console.log('boleto llamado desde "global" --- ', boleto)
restaurante()
Y deberás ver el llamado a las 3 variables con los 3 scopes: global, local y block
boleto llamado desde "global" --- BOLETO
paseVIPRestaurante llamado desde "restaurante" --- VIP-Restaurante
tickets llamado desde "restaurante" en la expresión if --- 10
BONUS
Si te fijas en estos ejemplos estuve declarando variables utilizando let
. ¿Por qué? Si ya estas familiarizado con const
, let
y var
ya tendrás una idea. Pero si no te lo cuento rápido.
Dependiendo de que keyword uses, obtendrás un comportamiento diferente del scope. let
y const
en este caso son la keywords que me permiten explicar el concepto de lexical scope de manera más directa. Utilizar var
tiene otras implicaciones.
¿Por qué no use const
? Pues no hay una razón en especial. Lancé una moneda y decidi ser consistente usando let
. No vaya a ser que alguien se me confunda si convino los 2.
Pero amig@ mío no sufras que si quieres conocer las implicaciones de usar const
, let
y var
, puedes dar click aquí.