返回

揭秘 JavaScript 执行机制:从执行上下文到执行栈

前端

站在技术博客创作专家的角度,JavaScript 作为一门高级的编程语言,其执行机制并不是简单明了,但我们却可以从执行上下文和执行栈两个概念出发,揭开 JavaScript 执行背后的神秘面纱。

理解执行上下文

在 JavaScript 中,执行上下文是一个抽象的概念,它代表了 JavaScript 代码执行的环境,而当前 JavaScript 代码正在运行的环境就是当前的执行上下文,一般可以简单地理解为一个全局执行上下文或一个函数执行上下文。

  • 全局执行上下文 :当 JavaScript 代码开始执行时,首先创建全局执行上下文,它负责处理全局变量和全局函数,并且是所有其他执行上下文(函数执行上下文)的父级。
  • 函数执行上下文 :当一个函数被调用时,会创建一个新的函数执行上下文,这个新创建的函数执行上下文会包含函数的参数、局部变量和函数内部的代码,同时也是全局执行上下文的子级,与全局执行上下文形成层级结构。

理解执行栈

执行栈是一个数据结构,它存储着所有被调用的函数执行上下文,当一个函数被调用时,它的执行上下文会被压入执行栈中,当函数执行完毕后,它的执行上下文会被从执行栈中弹出。

执行栈的工作方式:

  1. JavaScript 引擎首先创建一个全局执行上下文,并将它压入执行栈中。
  2. 当一个函数被调用时,一个新的执行上下文被创建并压入执行栈中,该执行上下文包含函数的参数、局部变量和函数内部的代码。
  3. JavaScript 引擎开始执行函数内部的代码。
  4. 当函数执行完毕后,它的执行上下文被从执行栈中弹出,控制权返回给调用它的函数。
  5. 如果调用它的函数也执行完毕,那么它的执行上下文也会被从执行栈中弹出,控制权返回给父级执行上下文。
  6. 当所有函数执行完毕后,全局执行上下文也被从执行栈中弹出,JavaScript 代码执行结束。

执行上下文和执行栈的关系:

  • 执行栈是一个先进后出的数据结构,每次函数被调用时,它的执行上下文都会被压入执行栈中,而当函数执行完毕后,它的执行上下文会被从执行栈中弹出。
  • 执行上下文和执行栈是 JavaScript 执行机制的核心组成部分,两者之间有着密切的关系,可以帮助理解变量提升、作用域、闭包等概念。

变量提升:

  • 在 JavaScript 中,变量提升是一个有趣且容易让人混淆的概念。
  • 当 JavaScript 引擎遇到一个变量声明时,它会将该变量提升到当前执行上下文的顶部,无论该变量是在代码的开头还是后面声明。
  • 例如:
function myFunction() {
  var a = 10;
  console.log(a); // 输出:10
  var b = 20;
  console.log(b); // 输出:20
}

myFunction();
  • 在这个例子中,变量 ab 都在函数 myFunction() 的开头声明,但是 a 被提升到函数执行上下文的顶部,因此当我们使用 console.log() 输出 a 的值时,它会输出 10

作用域:

  • 作用域是 JavaScript 中另一个重要的概念。
  • 作用域决定了变量的可访问性,它定义了变量可以在哪些地方被访问。
  • JavaScript 中有两种作用域:全局作用域和局部作用域。
  • 全局变量在全局执行上下文中声明,可以在任何地方访问。
  • 局部变量在函数执行上下文中声明,只能在该函数内部访问。
  • 例如:
var globalVariable = 10;

function myFunction() {
  var localVariable = 20;
  console.log(globalVariable); // 输出:10
  console.log(localVariable); // 输出:20
}

myFunction();

console.log(globalVariable); // 输出:10
console.log(localVariable); // 输出:错误:localVariable 在全局执行上下文中不可用
  • 在这个例子中,globalVariable 是一个全局变量,它可以在任何地方访问。localVariable 是一个局部变量,只能在函数 myFunction() 内部访问。当我们试图在全局执行上下文中访问 localVariable 时,我们会得到一个错误,因为 localVariable 在全局执行上下文中不可用。

闭包:

  • 闭包是 JavaScript 中一个高级的概念,它允许函数访问其父级函数的作用域,即使父级函数已经执行完毕。
  • 闭包可以被用来实现私有变量和私有方法。
  • 例如:
function createCounter() {
  var counter = 0;

  return function() {
    counter++;
    return counter;
  };
}

var myCounter = createCounter();

console.log(myCounter()); // 输出:1
console.log(myCounter()); // 输出:2
console.log(myCounter()); // 输出:3
  • 在这个例子中,createCounter() 函数返回一个新的函数,该函数可以访问其父级函数 createCounter() 的作用域,即使 createCounter() 函数已经执行完毕。
  • 这使得我们可以使用闭包来实现私有变量和私有方法。

希望这篇博文能帮助你理解 JavaScript 中的执行上下文和执行栈,掌握这些概念将使你成为一个更加优秀的 JavaScript 开发者。