阿里云主机折上折
  • 微信号
Current Site:Index > Execution context and variable object

Execution context and variable object

Author:Chuan Chen 阅读数:29797人阅读 分类: JavaScript

In JavaScript, execution context and variable object are core concepts for understanding how code operates. They determine the accessibility of variables and functions, the formation of the scope chain, and the order of code execution.

The Concept of Execution Context

An execution context is the environment in which JavaScript code is executed, containing information such as variables, function declarations, and the scope chain. A new execution context is created whenever a function is called. There are three types of execution contexts:

  1. Global Execution Context: Created when the code runs for the first time; there is only one global context.
  2. Function Execution Context: Created each time a function is called.
  3. Eval Execution Context: Created when code inside the eval function is executed (rarely used).
function foo() {
  console.log('Function execution context');
}
foo(); // A new execution context is created when the function is called

Lifecycle of an Execution Context

The lifecycle of an execution context consists of two phases:

  1. Creation Phase:

    • Create the Variable Object (VO)
    • Establish the scope chain
    • Determine the this binding
  2. Execution Phase:

    • Assign values to variables
    • Reference functions
    • Execute other code
function bar(a) {
  var b = 2;
  function c() {}
  var d = function() {};
}
bar(1);

During the creation phase of the bar function:

  • The parameter a is added to the variable object
  • The function declaration c is hoisted
  • Variables b and d are declared (but not yet assigned)

Variable Object (VO) and Activation Object (AO)

The variable object is the data scope associated with the execution context, storing variables and function declarations defined in the context.

  • Variable Object in Global Context: This is the global object (window in browsers)
  • Variable Object in Function Context: Called the Activation Object (AO), which includes variables, functions, and arguments
function example(x, y) {
  var z = 30;
  function inner() {}
  console.log(arguments); // Arguments object
}
example(10, 20);

In this example, the activation object contains:

  • arguments: {0:10, 1:20, length:2}
  • x: 10
  • y: 20
  • z: undefined (during the creation phase)
  • inner: Reference to the function

The Essence of Hoisting

Variable hoisting is essentially the result of how variable and function declarations are processed during the creation phase of the execution context:

console.log(a); // undefined
var a = 1;
console.log(b()); // "hello"
function b() {
  return 'hello';
}

The actual execution order is equivalent to:

function b() {
  return 'hello';
}
var a;
console.log(a);
a = 1;
console.log(b());

Formation of the Scope Chain

When looking up a variable, the search starts from the current context's variable object and moves up the scope chain:

var globalVar = 'global';
function outer() {
  var outerVar = 'outer';
  function inner() {
    var innerVar = 'inner';
    console.log(innerVar); // 'inner'
    console.log(outerVar); // 'outer'
    console.log(globalVar); // 'global'
  }
  inner();
}
outer();

Scope chain:

  1. inner's AO
  2. outer's AO
  3. Global VO

let/const and the Variable Object

Variables declared with let and const have block scope and are not added to the variable object like var:

function blockExample() {
  console.log(a); // ReferenceError
  let a = 1;
  {
    let b = 2;
    console.log(b); // 2
  }
  console.log(b); // ReferenceError
}

Closures and the Variable Object

Closures can access the variable object of an outer function:

function createCounter() {
  let count = 0;
  return {
    increment: function() {
      count++;
      return count;
    },
    get: function() {
      return count;
    }
  };
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.get()); // 1

Even after createCounter finishes executing, its variable object is still referenced by the closure and is not destroyed.

Execution Context Stack

The JavaScript engine uses an execution context stack (call stack) to manage execution contexts:

function first() {
  console.log('Entering first');
  second();
  console.log('Leaving first');
}
function second() {
  console.log('Entering second');
  third();
  console.log('Leaving second');
}
function third() {
  console.log('Entering third');
  console.log('Leaving third');
}
first();

Changes in the execution stack:

  1. Global context is pushed onto the stack
  2. first() is pushed onto the stack
  3. second() is pushed onto the stack
  4. third() is pushed onto the stack
  5. third() is popped off the stack
  6. second() is popped off the stack
  7. first() is popped off the stack

Special Behavior of the Variable Object

In some cases, the variable object behaves in noteworthy ways:

(function() {
  console.log(typeof foo); // "function"
  console.log(typeof bar); // "undefined"
  
  var foo = 'hello';
  function foo() {}
  var bar = function() {};
  
  console.log(typeof foo); // "string"
  console.log(typeof bar); // "function"
})();

This is because:

  1. Function declarations take precedence over variable declarations
  2. Variable assignments occur during the execution phase

Changes in Strict Mode

Strict mode affects the variable object in specific ways:

'use strict';
function strictExample() {
  undeclaredVar = 1; // ReferenceError
  console.log(arguments.callee); // TypeError
}
strictExample();

In strict mode:

  • Assigning to undeclared variables throws an error
  • The arguments object becomes non-modifiable

Variable Object and Memory Management

Understanding the variable object is important for memory management:

function createHugeArray() {
  var arr = new Array(1000000).fill('data');
  return function() {
    console.log(arr[0]);
  };
}
var leak = createHugeArray();
// Even after the function finishes executing, `arr` is still referenced by the closure and cannot be garbage collected

Dynamic Scope and the Variable Object

Although JavaScript has lexical scope, this provides dynamic scope-like behavior:

var obj = {
  prop: 'value',
  method: function() {
    console.log(this.prop);
  }
};
obj.method(); // "value"
var fn = obj.method;
fn(); // undefined (in non-strict mode)

Extensions to the Variable Object

ES6 introduced features that extend the concept of the variable object:

function spreadExample(...args) {
  let [first, ...rest] = args;
  const PI = 3.14;
  class Inner {}
  console.log(args.length);
}
spreadExample(1, 2, 3);

These new syntactic structures all influence the composition of the variable object.

Execution Context in Debugging

Developer tools allow observation of execution contexts:

function debugExample() {
  debugger;
  var a = 1;
  let b = 2;
}
debugExample();

In the debugger, you can see:

  • Variables in the current scope
  • Variables in closures
  • Global variables

Execution Context and the Event Loop

Execution contexts are closely related to the event loop mechanism:

console.log('Start');
setTimeout(function() {
  console.log('Timeout');
}, 0);
Promise.resolve().then(function() {
  console.log('Promise');
});
console.log('End');

Output order:

  1. "Start"
  2. "End"
  3. "Promise"
  4. "Timeout"

This is because different tasks create different execution contexts and enter different queues.

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:this绑定规则

下一篇:垃圾回收机制

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.