Saltearse al contenido

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.

1
function miFuncion() {
2
var miVariable = 10;
3
console.log(miVariable); // 10
4
}
5
6
miFuncion();
7
console.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.

1
var miVariable = 10;
2
3
function miFuncion() {
4
console.log(miVariable); // 10
5
}
6
7
miFuncion();
8
console.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.

1
let miVariable = "global";
2
3
function miFuncion() {
4
let miVariable = "local";
5
console.log(miVariable);
6
}
7
8
miFuncion(); // local
9
console.log(miVariable); // global

No obstante, no es posible “redeclarar” una variable con la palabra reservada let en el mismo contexto de ejecución.

1
let miVariable = "global";
2
3
function miFuncion() {
4
let miVariable = "local";
5
let miVariable = "nueva"; // SyntaxError: Identifier 'miVariable' has already been declared
6
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.

1
var miVariable = "global";
2
3
function miFuncion() {
4
var miVariable = "local";
5
var miVariable = "nueva";
6
console.log(miVariable); // nueva
7
}
8
9
miFuncion(); // nueva
10
console.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.

1
let miVariable = "global";
2
3
function miFuncion() {
4
let miVariable = "local";
5
6
if (true) {
7
let miVariable = "bloque";
8
console.log(miVariable); // bloque
9
}
10
console.log(miVariable); // local
11
}
12
13
miFuncion(); // bloque, local
14
console.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.

1
window;
1
this;

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.

1
window === 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.

1
var miVariable = 'global';
2
3
function 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:

    1. Se crean el objeto global window y la variable global this que apunta a window.
    2. Se asigna un espacio en memoria para la variable miVariable y la función logFn.
    3. La variable miVariable es inicializada con el valor undefined. La función logFn es colocada en memoria en su totalidad, pero no se ejecuta.
  • En la fase de ejecución:

    1. El valor 'global' es asignado a la variable miVariable.
    2. Como hemos definido la función, pero aún no la hemos llamado, la ejecución de la función no tiene lugar.

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.

1
var miVariable = 'global';
2
3
function logFn(mensaje) {
4
console.log(mensaje);
5
}
6
7
logFn('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:

1
FunctionExecutionContext {
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:

    1. Se crea un nuevo contexto de ejecución local FunctionExecutionContext.
    2. Se asigna un espacio en memoria para arguments.
    3. 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!'.
    4. 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!'.
    5. Se asigna un espacio en memoria para las variables y funciones declaradas dentro de la función.
  • En la fase de ejecución:

    1. 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.
    2. 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!'.

Bibliografía