Contexto de ejecución
En JavaScript, el contexto de ejecución es un entorno en el que se ejecuta el código y define el alcance de las variables y funciones que se pueden acceder en ese momento.
Esto es mejor conocido como ámbito o scope, y existen varios tipos de contextos de ejecución en JavaScript, pero los que más nos interesan son el contexto de ejecución global y el contexto de ejecución local.
Ámbito local
El ámbito local o local scope (FunctionExecutionContext
) establece que, las variables y funciones declaradas dentro de una función en particular, solo pueden ser accedidas desde dentro de ese mismo bloque de código correspondiente a dicha función.
1function miFuncion() {2 var miVariable = 10;3 console.log(miVariable); // 104}5
6miFuncion();7console.log(miVariable); // ReferenceError: miVariable is not defined
Es decir, que las variables y funciones declaradas mediante un scope local, no pueden ser accedidas desde fuera del bloque de código en el que fueron declaradas.
Ámbito global
El ámbito global o global scope (GlobalExecutionContext
) establece que, las variables y funciones declaradas fuera de una función o bloque de código, pueden ser accedidas de igual manera desde cualquier parte del código.
1var miVariable = 10;2
3function miFuncion() {4 console.log(miVariable); // 105}6
7miFuncion();8console.log(miVariable); // 10
Variables en contexto de ejecución
Como sabemos en JavaScript, podemos declarar variables mediante las palabras reservadas var
o let
. Dependiendo de la palabra reservada que utilicemos, tendremos un comportamiento diferente para cada contexto de ejecución.
La palabra reservada let
permitirá la definición de una variable con el mismo nombre en diferentes contextos de ejecución.
1let miVariable = "global";2
3function miFuncion() {4 let miVariable = "local";5 console.log(miVariable);6}7
8miFuncion(); // local9console.log(miVariable); // global
No obstante, no es posible “redeclarar” una variable con la palabra reservada let
en el mismo contexto de ejecución.
1let miVariable = "global";2
3function miFuncion() {4 let miVariable = "local";5 let miVariable = "nueva"; // SyntaxError: Identifier 'miVariable' has already been declared6 console.log(miVariable);7}
Por el contrario, la palabra reservada var
permite “redeclarar” una variable en el mismo contexto de ejecución. Lo que puede llevar a errores en la lógica de nuestro código si no tenemos cuidado.
1var miVariable = "global";2
3function miFuncion() {4 var miVariable = "local";5 var miVariable = "nueva";6 console.log(miVariable); // nueva7}8
9miFuncion(); // nueva10console.log(miVariable); // global
Ámbito de bloque
Por último, haremos mención al ámbito de bloque o block scope (BlockExecutionContext
), el cual se refiere a las variables declaradas con let
y const
dentro de un bloque de código (cualquier grupo de sentencias entre llaves {}
).
Este tipo de ámbito permite la definición de variables con el mismo nombre en bloques de código anidados, pero no en el mismo bloque de código. Es decir, el mismo comportamiento que let
para el ámbito local pero pudiendo ser utilizado en cualquier otro bloque de código como ser una estructura de control if
, for
, while
, entre otros.
1let miVariable = "global";2
3function miFuncion() {4 let miVariable = "local";5
6 if (true) {7 let miVariable = "bloque";8 console.log(miVariable); // bloque9 }10 console.log(miVariable); // local11}12
13miFuncion(); // bloque, local14console.log(miVariable); // global
Como podemos ver, este ámbito cuenta con las mismas características que el ámbito local, por lo cual algunas veces se le considera como un subtipo de este. Dependiendo de la bibliografía consultada, se puede encontrar que los únicos ámbitos en JavaScript son el ámbito global y el ámbito local, abarcando este último todas las declaraciones de variables (var
, let
y const
) y funciones para cualquier nivel de anidamiento.
En general, haremos referencia únicamente al ámbito global y al ámbito local de aquí en adelante.
Fases de contextos de ejecución
El contexto de ejecución en JavaScript es un proceso que evalúa(crea o declara) y ejecuta el código en base al entorno en el que se encuentra (global o local).
Entonces, podemos afirmar que cada contexto de ejecución tiene dos fases: creación y ejecución.
Fase de creación en el contexto global
En la fase de creación, la primera tarea que realiza el contexto de ejecución global es crear el objeto global window
(en el caso de un navegador web) y una variable global this
que apunta a ese objeto.
Luego, si hay alguna variable o función declarada en el código, se asigna un espacio en memoria para cada una de ellas. No obstente, las variables son inicializadas con el valor undefined
y las funciones son almacenadas en su totalidad.
Fase de ejecución en el contexto global
Esta fase se caracteriza porque es el momento en el que se ejecuta el código como tal, de ahí su nombre.
Aquí es donde se asignan los valores a las variables globales. No obstante, no se asignan valores a las variables locales, ya que estas correspondan al contexto de ejecución de funciones FunctionExecutionContext
.
Debemos resaltar que, la invocaión de funciones no se realiza en este contexto de ejecución.
Ejemplos
Definiremos un archivo index.html
y un archivo index.js
vacío con el cual estudiaremos el comportamiento por defecto de un script en un navegador web.
1<!DOCTYPE html>2<html lang="en">3<head>4 <meta charset="UTF-8">5 <meta http-equiv="X-UA-Compatible" content="IE=edge">6 <meta name="viewport" content="width=device-width, initial-scale=1.0">7 <title>Document</title>8 <script src='./index.js'></script>9</head>10<body>11 Cargando script vacío...12</body>13</html>
Al abrir el archivo index.html
en un navegador web e inspeccionar la consola, analizaremos la variable this
y el objeto global window
.
1window;
1this;
Veremos que al comparar ambos objetos, son iguales. Esto se debe a que this
apunta al objeto global window
en el contexto de ejecución global como se mencionó anteriormente.
1window === this
Con este pequeño ejemplo, podemos afirmar que el contexto de ejecución global se crea cuando cargamos un archivo JavaScript, incluso cuando está vacío.
Ahora, para estudiar el comportamiento de las variables y funciones en el contexto de ejecución global, definiremos una variable y una función en el archivo index.js
.
1var miVariable = 'global';2
3function logFn() {4 console.log(this.miVariable);5}
Esta vez, no podemos ver el proceso realizado en cada fase, pero si podemos definir paso por paso lo que sucede en el contexto de ejecución global.
-
En la fase de creación:
- Se crean el objeto global
window
y la variable globalthis
que apunta awindow
. - Se asigna un espacio en memoria para la variable
miVariable
y la funciónlogFn
. - La variable
miVariable
es inicializada con el valorundefined
. La funciónlogFn
es colocada en memoria en su totalidad, pero no se ejecuta.
- Se crean el objeto global
-
En la fase de ejecución:
- El valor
'global'
es asignado a la variablemiVariable
. - Como hemos definido la función, pero aún no la hemos llamado, la ejecución de la función no tiene lugar.
- El valor
Fase de creación en el contexto local
Cuando invocamos una función, se crea un nuevo contexto de ejecución local FunctionExecutionContext
pasa por las mismas fases que el contexto de ejecución global, creación y ejecución.
Para analizar este comportamiento, invocaremos la función logFn
definida en el ejemplo anterior.
1var miVariable = 'global';2
3function logFn(mensaje) {4 console.log(mensaje);5}6
7logFn('Hola, mundo!');
En la fase de creación en el contexto local, se asigna un espacio en memoria para la variable arguments
, la cual es inicializada con un objeto similar a un array que contiene los argumentos pasados a la función.
En este caso, la función logFn
recibe un argumento 'Hola, mundo!'
, por lo que arguments
tendrá un elemento con el valor 'Hola, mundo!'
.
Gráficamente, el contexto de ejecución local se vería de la siguiente manera:
1FunctionExecutionContext {2 arguments: { 0: 'Hola, mundo!', length: 1 },3}
Si analizamos el valor de this
en el navegador web, veremos que este tiene entre sus propiedades al identificador miVariable
con el valor 'global'
y la función logFn
con una propiedad arguments
, entre otras.
Por último, debemos tener en cuenta que, cada argumento pasado a la función es almacenado en una nueva variable local con el mismo nombre que el argumento. Estas variables son accesibles dentro del entorno de la función. En este caso, la variable mensaje
sería declarada e inicializada con el valor 'Hola, mundo!'
.
Cabe destacar que, todas las variables y funciones declaradas dentro de la función
logFn
también son declaradas en la fase de creación del contexto de ejecución local, pero no son inicializadas con un valor hasta la fase de ejecución.
Fase de ejecución en el contexto local
La fase de ejecución en el contexto local tiene acceso al valor arguments
definido en la fase de creación, así como a las variables y funciones declaradas en la función.
En esta fase, se asignan los valores a las variables y funciones declaradas dentro de la función, incluyendo arguments
. En nuestro ejemplo, logFn
no tiene variables ni funciones declaradas en el cuerpo de la misma, por lo que solo se ejecuta únicamente la función console.log(mensaje)
con el argumento 'Hola, mundo!'
.
Este proceso desemboca en la creación de un nuevo contexto de ejecución local, esta vez para console.log(mensaje)
, que pasa por las mismas fases de creación y ejecución.
En resumen, el contexto de ejecución local para una función se crea cuando la función es invocada y pasa por las fases de creación y ejecución:
-
En la fase de creación:
- Se crea un nuevo contexto de ejecución local
FunctionExecutionContext
. - Se asigna un espacio en memoria para
arguments
. arguments
es inicializada con un objeto similar a un array que contiene los argumentos pasados a la función. En este caso,arguments
contiene el argumento'Hola, mundo!'
.- Se declaran las variables para cada argumento pasado a la función. En este caso, se declara la variable
mensaje
con el valor'Hola, mundo!'
. - Se asigna un espacio en memoria para las variables y funciones declaradas dentro de la función.
- Se crea un nuevo contexto de ejecución local
-
En la fase de ejecución:
- Se asignan los valores a las variables declaradas dentro de la función. En este caso, no hay variables declaradas en la función
logFn
. - Se ejecuta todas las sentencias de código en la función. En este caso, se ejecuta la función
console.log(mensaje)
con el argumento'Hola, mundo!'
.
- Se asignan los valores a las variables declaradas dentro de la función. En este caso, no hay variables declaradas en la función