Si usted es un desarrollador de JavaScript o desea convertirse en uno, esto significa que debe comprender los mecanismos internos para ejecutar el código JS. En particular, es absolutamente necesario comprender cuál es el contexto de ejecución y la pila de llamadas para dominar otros conceptos de JavaScript, como elevar variables, alcance y cierre. El material, cuya traducción publicamos hoy, está dedicado al contexto de ejecución y la pila de llamadas en JavaScript.

Contexto de ejecución
El contexto de ejecución es, en términos simplificados, un concepto que describe el entorno en el que se ejecuta el código JavaScript. El código siempre se ejecuta dentro de un contexto.
▍ Ejecutar tipos de contexto
JavaScript tiene tres tipos de contextos de ejecución:
- Contexto de ejecución global. Este es el contexto básico de ejecución predeterminado. Si algún código no está dentro de ninguna función, entonces este código pertenece al contexto global. El contexto global se caracteriza por la presencia de un objeto global, que, en el caso del navegador, es el objeto de
window
, y el hecho de que this
apunta a este objeto global. Un programa solo puede tener un contexto global. - Contexto de ejecución de funciones. Cada vez que se llama a una función, se crea un nuevo contexto para ella. Cada función tiene su propio contexto de ejecución. Un programa puede tener simultáneamente muchos contextos para ejecutar funciones. Al crear un nuevo contexto para la ejecución de una función, pasa por una cierta secuencia de pasos, que discutiremos a continuación.
- El contexto de ejecución de la función
eval
. El código ejecutado dentro de la función eval
también tiene su propio contexto de ejecución. Sin embargo, la función eval
se usa muy raramente, por lo que aquí no hablaremos sobre este contexto de ejecución.
Pila de ejecución
La pila de ejecución, que también se llama pila de llamadas, es la pila LIFO que se utiliza para almacenar contextos de ejecución creados durante la ejecución del código.
Cuando el motor JS comienza a procesar el script, crea un contexto de ejecución global y lo coloca en la pila actual. Cuando se detecta un comando para llamar a una función, el motor crea un nuevo contexto de ejecución para esta función y lo coloca en la parte superior de la pila.
El motor realiza una función cuyo contexto de ejecución está en la parte superior de la pila. Cuando se completa la función, su contexto se elimina de la pila y el control se transfiere al contexto que se encuentra en el elemento anterior de la pila.
Exploraremos esta idea con el siguiente ejemplo:
let a = 'Hello World!'; function first() { console.log('Inside first function'); second(); console.log('Again inside first function'); } function second() { console.log('Inside second function'); } first(); console.log('Inside Global Execution Context');
Así es como cambiará la pila de llamadas cuando se ejecute este código.
Estado de la pila de llamadasCuando el código anterior se carga en el navegador, el motor de JavaScript crea un contexto de ejecución global y lo coloca en la pila de llamadas actual. Al realizar una llamada a la
first()
función
first()
, el motor crea un nuevo contexto para esta función y la coloca en la parte superior de la pila.
Cuando se llama a la
second()
función
second()
desde la
first()
función
first()
, se crea un nuevo contexto de ejecución para esta función y también se inserta en la pila. Después de que la
second()
función
second()
completa su trabajo, su contexto se elimina de la pila y el control se transfiere al contexto de ejecución ubicado en la pila debajo de ella, es decir, al contexto de la
first()
función
first()
.
Cuando sale la
first()
función
first()
, su contexto se extrae de la pila y el control se transfiere al contexto global. Después de ejecutar todo el código, el motor recupera el contexto de ejecución global de la pila actual.
Acerca de crear contextos y ejecutar código
Hasta ahora, hablamos sobre cómo el motor JS gestiona los contextos de ejecución. Ahora hablemos sobre cómo se crean los contextos de ejecución y qué les sucede después de que se crean. En particular, estamos hablando de la etapa de creación del contexto de ejecución y la etapa de ejecución del código.
▍ Etapa de creación del contexto de ejecución
Antes de ejecutar el código JavaScript, se crea el contexto de ejecución. En el proceso de su creación, se realizan tres acciones:
- Este valor se determina y
this
(este enlace) está vinculado. - Se
LexicalEnvironment
componente LexicalEnvironment
. - Se crea el componente
VariableEnvironment
.
Conceptualmente, el contexto de ejecución se puede representar de la siguiente manera:
ExecutionContext = { ThisBinding = <this value>, LexicalEnvironment = { ... }, VariableEnvironment = { ... }, }
Esta unión
En el contexto de ejecución global,
this
contiene una referencia al objeto global (como ya se mencionó, en el navegador es un objeto de
window
).
En el contexto de la ejecución de la función, el valor de
this
depende de cómo se llamó a la función. Si se llama como método de un objeto, entonces el valor de
this
vinculado a este objeto. En otros casos,
this
vinculado a un objeto global o establecido en
undefined
(en modo estricto). Considere un ejemplo:
let foo = { baz: function() { console.log(this); } } foo.baz(); // 'this' 'foo', 'baz' // 'foo' let bar = foo.baz; bar(); // 'this' window, //
Entorno léxico
De acuerdo con
la especificación ES6, Lexical Environment es un término que se utiliza para definir la relación entre los identificadores y las variables y funciones individuales en función de la estructura de la anidación léxica del código ECMAScript. El entorno léxico consta de un Registro de entorno y una referencia al entorno léxico externo, que puede ser
null
.
En pocas palabras, un entorno léxico es una estructura que almacena información sobre la correspondencia de identificadores y variables. Aquí, por "identificador" se entiende el nombre de una variable o función, y por "variable" es una referencia a un objeto específico (incluida una función) o un valor primitivo.
En el entorno léxico hay dos componentes:
- Registro de un entorno. Aquí es donde se almacenan las declaraciones de variables y funciones.
- Enlace al entorno externo. La presencia de dicho enlace indica que el entorno léxico tiene acceso al entorno léxico principal (ámbito).
Hay dos tipos de entornos léxicos:
- El entorno global (o el contexto de ejecución global) es un entorno léxico que no tiene un entorno externo. La referencia del entorno global al entorno externo es
null
. En el entorno global (en el registro del entorno), las entidades de lenguaje integradas (como Object
, Array
, etc.) están disponibles y están asociadas con el objeto global, también hay variables globales definidas por el usuario. El valor de this
en este entorno apunta a un objeto global. - El entorno de la función en la que, en el registro del entorno, se almacenan las variables declaradas por el usuario. La referencia al entorno externo puede indicar tanto un objeto global como una función externa a la función en cuestión.
Hay dos tipos de registros de entorno:
- Un registro de entorno declarativo que almacena variables, funciones y parámetros.
- Un registro de objeto de entorno que se utiliza para almacenar información sobre variables y funciones en un contexto global.
Como resultado, en un entorno global, un registro de entorno está representado por un registro de entorno de objeto, y en un entorno de función, por un registro de entorno declarativo.
Tenga en cuenta que en el entorno de la función, el registro declarativo del entorno también contiene el objeto de
arguments
, que almacena la correspondencia entre los índices y los valores de los argumentos pasados a la función, e información sobre el número de tales argumentos.
El entorno léxico se puede representar como el siguiente pseudocódigo:
GlobalExectionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Object", // } outer: <null> } } FunctionExectionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // } outer: < > } }
Variables de entorno
Un entorno variable también es un entorno léxico cuyo registro de entorno almacena los enlaces creados utilizando los comandos
VariableStatement
en el contexto de ejecución actual.
Dado que el entorno de las variables también es un entorno léxico, posee todas las propiedades descritas anteriormente del entorno léxico.
En ES6, hay una diferencia entre los componentes
LexicalEnvironment
y
VariableEnvironment
. Consiste en el hecho de que el primero se usa para almacenar declaraciones de funciones y variables declaradas usando las palabras clave
let
y
const
, y el segundo se usa solo para almacenar enlaces de variables declarados usando la palabra clave
var
.
Considere ejemplos que ilustran lo que acabamos de discutir:
let a = 20; const b = 30; var c; function multiply(e, f) { var g = 20; return e * f * g; } c = multiply(20, 30);
Una representación esquemática del contexto de ejecución para este código se verá así:
GlobalExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type: "Object", // a: < uninitialized >, b: < uninitialized >, multiply: < func > } outer: <null> }, VariableEnvironment: { EnvironmentRecord: { Type: "Object", // c: undefined, } outer: <null> } } FunctionExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // Arguments: {0: 20, 1: 30, length: 2}, }, outer: <GlobalLexicalEnvironment> }, VariableEnvironment: { EnvironmentRecord: { Type: "Declarative", // g: undefined }, outer: <GlobalLexicalEnvironment> } }
Como probablemente haya notado, las variables y constantes declaradas usando las palabras clave
let
y
const
no tienen valores asociados, y las variables declaradas usando la palabra clave
var
se establecen como
undefined
.
Esto es así porque durante la creación del contexto, el código busca declaraciones de variables y funciones, mientras que las declaraciones de funciones se almacenan completamente en el entorno. Los valores de las variables, cuando se usa
var
, se establecen como
undefined
, y cuando se usa
let
o
const
permanecen sin inicializar.
Es por eso que puede acceder a las variables declaradas con
var
antes de que se declaren (aunque no estarán
undefined
), pero cuando intenta acceder a las variables o constantes declaradas con
let
y
const
ejecutadas antes de que se declaren, se produce un error .
Lo que acabamos de describir se llama "variables de elevación". Las declaraciones de variables "suben" a la parte superior de su alcance léxico antes de realizar operaciones de asignación de valores.
▍ Etapa de ejecución del código
Esta es quizás la parte más simple de este material. En esta etapa, los valores se asignan a las variables y se ejecuta el código.
Tenga en cuenta que si, durante la ejecución del código, el motor JS no puede encontrar el valor de la variable declarada utilizando la palabra clave
let
en el lugar de la declaración, asignará a esta variable el valor
undefined
.
Resumen
Acabamos de discutir los mecanismos internos para ejecutar código JavaScript. Aunque para ser un muy buen desarrollador de JS, no es necesario saber todo esto, si comprende algo de los conceptos anteriores, lo ayudará a lidiar mejor y más profundamente con otros mecanismos del lenguaje, como elevar variables, alcance, Cortocircuitos.
Estimados lectores! ¿Qué más crees que aparte del contexto de ejecución y la pila de llamadas son útiles para que los desarrolladores de JavaScript sepan?
