JavaScript的执行上下文和执行栈

在 JavaScript 中,执行上下文(Execution Context)和执行栈(Execution Stack)是理解 JavaScript 代码执行过程中的两个非常关键的概念。它们是 JavaScript 引擎在运行代码时,管理执行流程和变量作用域的核心机制。下面将详细解释这两个概念。

1. 执行上下文(Execution Context)

定义:

执行上下文是 JavaScript 代码执行时的环境,决定了代码如何被解析和执行。每当 JavaScript 函数被调用、脚本被加载时,都会创建一个新的执行上下文。

执行上下文的类型:

JavaScript 中主要有三种类型的执行上下文:

  1. 全局执行上下文

    • 默认创建的执行上下文,代码最外层的上下文。
    • 在浏览器中,this 指向 window 对象。
    • 任何未定义在函数内的变量或函数,都会被添加到全局上下文中。
  2. 函数执行上下文

    • 每次调用一个函数时,都会为该函数创建一个新的执行上下文。
    • 每个函数有自己独立的上下文,并且在执行过程中会有自己的变量、参数和内部函数。
  3. eval 执行上下文(较少用):

    • 当使用 eval() 函数时,JavaScript 会将字符串内容当作代码执行,这时会创建一个独立的执行上下文。
执行上下文的生命周期:

每个执行上下文的生命周期大致可以分为两个阶段:

  1. 创建阶段

    • 变量对象(Variable Object)/ 词法环境(Lexical Environment)
      • 变量、函数声明和参数被存储在执行上下文的变量对象中。
    • 作用域链(Scope Chain)
      • 作用域链确定了当前执行代码在哪些环境中可以访问变量。
    • this 绑定
      • this 会根据上下文不同绑定到不同的对象(如全局对象或调用该函数的对象)。
  2. 执行阶段

    • 变量赋值、函数执行,代码依次执行。
示例:
var name = 'Alice';

function sayHello() {
  var greeting = 'Hello';
  console.log(greeting + ', ' + name);
}

sayHello();
  1. 全局执行上下文

    • 创建变量 name,并赋值为 'Alice'
    • sayHello 函数被声明但尚未执行。
  2. 函数执行上下文(当调用 sayHello() 时):

    • 创建 greeting 变量,并赋值为 'Hello'
    • 执行 console.log,通过作用域链可以访问全局变量 name

2. 执行栈(Execution Stack)

定义:

执行栈(也称作调用栈,Call Stack)是用来管理多个执行上下文的栈结构。当一个函数被调用时,其对应的执行上下文会被推入执行栈;当函数执行完毕后,执行上下文会从栈中弹出。

执行栈的特点:
  • 后进先出(LIFO):执行栈遵循后进先出的原则,最后进入的执行上下文最先被执行完毕。
  • 全局执行上下文位于栈底:全局执行上下文是整个程序最开始执行的环境,它始终处于栈底。
执行过程:
  • 当 JavaScript 引擎开始执行代码时,会首先创建全局执行上下文,并将其推入执行栈。
  • 当函数被调用时,会为该函数创建一个新的执行上下文,并推入栈中。
  • 当函数执行完毕时,执行上下文会从栈中弹出,控制权返回到上一个执行上下文。
示例:
function multiply(x, y) {
  return x * y;
}

function square(n) {
  return multiply(n, n);
}

function printSquare(num) {
  var result = square(num);
  console.log(result);
}

printSquare(5);
执行栈过程:
  1. 全局执行上下文被创建并推入执行栈。
  2. 执行到 printSquare(5),为 printSquare 创建执行上下文并推入栈中。
  3. printSquare 中调用 square(5),创建 square 的执行上下文并推入栈中。
  4. square(5) 中调用 multiply(5, 5),创建 multiply 的执行上下文并推入栈中。
  5. multiply 函数执行完毕,执行上下文从栈中弹出,返回控制权给 square
  6. square 执行完毕,执行上下文弹出,返回控制权给 printSquare
  7. printSquare 执行完毕,执行上下文弹出,返回控制权给全局执行上下文。
  8. 最终,程序执行完毕,全局执行上下文也会弹出。
可视化:
执行栈变化:
1. [ 全局执行上下文 ]
2. [ 全局执行上下文, printSquare 执行上下文 ]
3. [ 全局执行上下文, printSquare 执行上下文, square 执行上下文 ]
4. [ 全局执行上下文, printSquare 执行上下文, square 执行上下文, multiply 执行上下文 ]
5. [ 全局执行上下文, printSquare 执行上下文, square 执行上下文 ]
6. [ 全局执行上下文, printSquare 执行上下文 ]
7. [ 全局执行上下文 ]
8. [ ]

3. 执行上下文与执行栈的关系

  • 执行上下文是 JavaScript 代码的执行环境。每当 JavaScript 引擎执行代码时,会为当前代码块(如全局代码、函数调用)创建一个新的执行上下文。
  • 执行栈是管理这些执行上下文的结构。函数调用会在执行栈中按顺序推入新的执行上下文,执行完毕后从栈中弹出。

4. 执行上下文和执行栈的使用场景

  • 递归函数:递归调用函数时,每次递归都会推入一个新的执行上下文,形成多个嵌套的执行栈帧。当递归结束时,执行上下文逐个弹出。

    示例:

    function factorial(n) {
      if (n === 1) return 1;
      return n * factorial(n - 1);
    }
    
    factorial(5);
    

    每次递归调用 factorial 都会创建一个新的执行上下文并推入执行栈。

  • 异步代码执行:异步代码(如 setTimeout)并不会立即创建新的执行上下文,而是被放入任务队列中,当栈清空后再执行。

    示例:

    console.log('Start');
    
    setTimeout(() => {
      console.log('Async task');
    }, 0);
    
    console.log('End');
    

    输出顺序是:

    Start
    End
    Async task
    

    因为 setTimeout 的回调在全局上下文结束后才会被执行。

5. 总结

  • 执行上下文:是 JavaScript 代码的运行环境,决定了代码执行时的变量、作用域和 this 绑定。主要有三种类型:全局执行上下文、函数执行上下文和 eval 执行上下文。

  • 执行栈:是管理执行上下文的栈结构,遵循后进先出的原则,用来记录函数调用和返回的顺序。

  • 执行栈和执行上下文是 JavaScript 代码运行的核心机制,掌握它们有助于理解 JavaScript 的函数调用、作用域链和异步执行等高级特性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端李易安

打赏1元鼓励作者

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值