Scope léxico y cadena de scopes en JavaScript | Nicolás Garzón
JavaScript utiliza scope léxico : una función puede acceder a los nombres disponibles en el lugar donde fue escrita, no en el lugar desde donde se llama.
JavaScript
Copiar const language = "JavaScript" ;
function showLanguage ( ) {
console. log ( language) ;
} La estructura de scopes puede determinarse observando dónde están escritas las declaraciones.
JavaScript
Copiar const application = "DomiSys" ;
function createReport ( ) {
const section = "Inventory" ;
function printTitle ( ) {
console. log ( application, section) ;
}
printTitle ( ) ;
} printTitle está escrita dentro de createReport, que a su vez está dentro del scope superior.
Su cadena de búsqueda es:
Texto
Copiar printTitle
↓
createReport
↓
scope superior del módulo o scriptPor eso encuentra tanto section como application.
Cuando JavaScript encuentra un nombre como section, sigue este proceso conceptual:
Busca el binding en el entorno actual.
Si no existe, revisa el entorno exterior.
Continúa hacia fuera hasta encontrarlo.
Si alcanza el final sin encontrarlo, produce un ReferenceError.
JavaScript
Copiar const first = "A" ;
function outer ( ) {
const second = "B" ;
function inner ( ) {
const third = "C" ;
console. log ( first, second, third) ;
}
inner ( ) ;
}
third se encuentra localmente.
second se encuentra en outer.
first se encuentra en el scope superior.
Un scope exterior no puede entrar en un scope interior.
JavaScript
Copiar function outer ( ) {
function inner ( ) {
const secret = "local" ;
}
console. log ( secret) ;
} Tampoco una función hermana puede acceder directamente a los locales de otra.
JavaScript
Copiar function parent ( ) {
function firstChild ( ) {
const value = 10 ;
}
function secondChild ( ) {
console. log ( value) ;
}
} JavaScript
Copiar const value = "global" ;
function printValue ( ) {
console. log ( value) ;
}
function run ( ) {
const value = "local" ;
printValue ( ) ;
}
run ( ) ; Aunque printValue se llama dentro de run, no utiliza el value local de run. Fue escrita en el scope superior, así que su referencia exterior apunta a ese entorno.
JavaScript no usa scope dinámico para resolver estas variables.
JavaScript
Copiar const taxRate = 0.19 ;
function calculateTotal ( subtotal ) {
const tax = subtotal * taxRate;
return subtotal + tax;
} Dentro de calculateTotal:
subtotal se encuentra como parámetro local.
tax se encuentra localmente.
taxRate se encuentra en el scope exterior.
JavaScript
Copiar function createGreeting ( prefix ) {
return function greet ( name ) {
return ` ${ prefix} , ${ name} ` ;
} ;
}
const sayHello = createGreeting ( "Hello" ) ;
sayHello ( "Nicolás" ) ; greet continúa accediendo a prefix incluso después de que createGreeting terminó. Ese comportamiento recibe el nombre de closure y se estudiará en una nota específica.
JavaScript
Copiar function createLogger ( label ) {
return function log ( message ) {
console. log ( ` [ ${ label} ] ${ message} ` ) ;
} ;
}
const inventoryLogger = createLogger ( "Inventory" ) ;
const ordersLogger = createLogger ( "Orders" ) ;
inventoryLogger ( "Low stock" ) ;
ordersLogger ( "New order" ) ; Aunque ambas funciones se crean a partir del mismo código, cada llamada a createLogger produce un entorno diferente con su propio label.
La cadena de scopes resuelve identificadores:
JavaScript
Copiar console. log ( user) ; Después de encontrar user, el acceso .profile.name utiliza las reglas de objetos y prototipos:
JavaScript
Copiar console. log ( user. profile. name) ; Son dos búsquedas diferentes:
Encontrar el binding user mediante la cadena de scopes.
Encontrar propiedades dentro del objeto mediante acceso propio y cadena de prototipos.
No conviene confundir la scope chain con la prototype chain .
JavaScript
Copiar function processOrder ( order ) {
const orderId = order. id;
setTimeout ( function notify ( ) {
console. log ( ` Order ${ orderId} processed ` ) ;
} , 1000 ) ;
} notify puede leer orderId porque fue creada dentro de processOrder.
El temporizador decide cuándo se llamará la función, pero no cambia qué scope exterior conserva.
JavaScript
Copiar
let count = 0 ;
export function increment ( ) {
count++ ;
return count;
} increment puede acceder al binding privado count del módulo. Otros módulos no pueden modificarlo directamente si no se exporta.
Pensar que una función utiliza variables del lugar desde donde fue llamada.
Confundir scope léxico con this.
Creer que una función exterior puede acceder a variables internas.
Confundir la cadena de scopes con la cadena de prototipos.
Depender de variables exteriores sin hacer evidente esa dependencia.
Crear callbacks en un bucle sin comprender qué binding capturan.
Suponer que una operación asíncrona pierde su scope exterior.
JavaScript resuelve nombres según el lugar donde se escribió el código.
La búsqueda comienza en el entorno actual y continúa hacia fuera.
La cadena nunca busca dentro de funciones hijas o hermanas.
El lugar desde donde se llama una función no modifica su scope léxico.
Los parámetros y variables locales son bindings del entorno de función.
Las funciones conservan una referencia a su entorno exterior.
Scope chain y prototype chain resuelven problemas diferentes.
¿Qué imprime este código?
JavaScript
Copiar const name = "Global" ;
function printName ( ) {
console. log ( name) ;
}
function execute ( ) {
const name = "Local" ;
printName ( ) ;
}
execute ( ) ; Respuesta Texto
Copiar GlobalprintName fue declarada en el scope superior. El name local de execute no forma parte de su cadena léxica.
Shadowing explica qué ocurre cuando un scope interior declara un binding con el mismo nombre que uno exterior.