揭秘 JavaScript:Execution Contexts、Hoisting、Scopes 与 Closures

发布: (2026年1月20日 GMT+8 18:42)
4 min read
原文: Dev.to

Source: Dev.to

什么是执行上下文?

执行上下文帮助 JavaScript 引擎管理代码复杂度,类似函数帮助作者管理编写复杂度。引擎会创建两种类型的执行上下文:

  • 全局执行上下文 – 在启动时创建。
  • 函数执行上下文 – 每次调用函数时创建。

每个上下文都有 创建阶段执行阶段

  • 创建:引擎设置全局对象(浏览器中的 window,Node 中的 global),this,为变量/函数分配内存,将变量初始化为 undefined,并完整加载函数声明。

提升(Hoisting)解释

提升发生在创建阶段。变量声明被设为 undefined,而函数声明在逐行执行之前就已完整加载。这解释了下面代码为何会输出 undefined

console.log('name: ', name);          // undefined
console.log('getUser: ', getUser);    // [Function: getUser]

var name = 'Tyler';

function getUser() {
  return { name: name };
}

函数声明 会被完整提升,而变量则不会。

函数执行上下文

当函数被调用时,会创建一个新的 函数执行上下文。它包括:

  • 一个 arguments 对象(而不是全局对象)
  • 它自己的 this 绑定
  • 被提升的局部变量和函数声明

每一次调用都会把新的上下文压入调用栈;执行完毕后弹出栈,体现了 JavaScript 的单线程特性。

参数会成为局部变量,内部变量则保留在各自的上下文中。例如,将 handle 传递给 getURL(handle) 时,它会在局部与全局一起存在。

作用域与作用域链

作用域 定义了当前执行上下文中变量的可访问性。局部变量在其上下文被弹出后无法再访问,若此时尝试使用会抛出 ReferenceError

function foo() {
  var bar = 42;
}
foo();
console.log(bar); // ReferenceError

如果在局部找不到变量,引擎会沿着 作用域链 向上查找父级上下文,直至全局作用域。当不同上下文中存在同名变量(如 name)时,引擎会先在本地解析,从而产生 undefinedJordynJakeTyler 等不同的输出,取决于查找顺序。

闭包实战

闭包 使得内部函数即使在外部(父)函数执行完毕后,仍能保留对外部作用域中变量的访问。下面的例子中,返回的 inner 函数保持对 x 的引用:

function makeAdder(x) {
  return function inner(y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
console.log(add5(2)); // 7

inner 函数对 x 形成闭包,使其值在多次调用中得以保留。

欲了解更多信息,请访问:
Ultimate Guide to Execution Contexts, Hoisting, Scopes, and Closures in JavaScript

Back to Blog

相关文章

阅读更多 »

在 Event Loop 之前会发生什么

JavaScript 代码并不会在任务出现在调用栈的那一刻就被引擎本地执行。大多数文章只关注 Event Loop 的工作原理,却忽略了……