本文档涵盖了 JavaScript 中词法作用域和闭包的核心概念——这是组织、访问和保留代码中变量的基本机制。这些概念对于理解 JavaScript 的行为以及编写有效、可维护的代码至关重要。本材料对应于“You Don't Know JS Yet”系列的第二本书。
有关更广泛的 JavaScript 语言基础知识,请参阅概述。有关 JavaScript 类型系统的具体信息,请参阅类型与语法。
作用域是决定变量在程序中何处以及如何被访问的一组规则。JavaScript 使用词法作用域,这意味着作用域是在编译时根据变量和作用域块在代码中的书写位置来确定的。
从本质上讲,JavaScript 的处理包含两个阶段:
理解这种双阶段处理对于掌握 JavaScript 如何处理变量至关重要。
来源:scope-closures/ch1.md17-21 scope-closures/ch1.md77-81
词法作用域可以被可视化为一系列嵌套的气泡或容器,每个都代表一个变量所在的范围。
此图展示了书中示例的嵌套作用域结构。每个矩形都是一个包含自身变量的作用域“气泡”
students、getStudentName 和 nextStudentstudentID 参数student 循环变量当 JavaScript 引擎需要查找变量时,它会从当前作用域开始,如果找不到则向外查找——这个过程称为“作用域查找”。
来源:scope-closures/ch2.md15-47 scope-closures/ch2.md50-56 scope-closures/ch2.md75-98
当作用域嵌套时,它们会形成一个链条,JavaScript 引擎在查找变量时会遍历这个链条。
作用域链的工作方式如下:
ReferenceError此查找过程是单向的,始终沿着作用域链向上/向外移动,一旦找到第一个匹配的变量就会停止。
来源:scope-closures/ch3.md18-19 scope-closures/ch2.md213-237
当链中的多个作用域存在相同的变量名时,内部变量会“遮蔽”外部变量,使得外部变量在该内部作用域中无法访问。
在这个例子中,参数 studentName 遮蔽了全局的 studentName,创建了两个具有相同名称但在不同作用域中的独立变量。
了解变量在其作用域中何时可用,对于编写可预测的 JavaScript 代码至关重要。
提升(Hoisting)是一种行为,在编译过程中,变量和函数的声明被概念上地“移动”到其包含作用域的顶部。
不同的声明类型被提升的方式不同:
var 声明会被提升并自动初始化为 undefinedlet 和 const 声明会被提升,但不会被初始化(TDZ)这解释了为什么函数可以在其声明出现在代码之前被调用。
来源:scope-closures/ch5.md11-33 scope-closures/ch5.md91-103
使用 let 和 const 声明时,变量在作用域开始到声明被处理的这段时间内,存在于所谓的“暂时性死区”中。
在 TDZ 期间,尝试访问变量会导致 ReferenceError,即使变量实际上“存在”于作用域中。
来源:scope-closures/ch5.md147-155 scope-closures/ch1.md129-151
闭包是 JavaScript 最强大的特性之一。当一个函数“记住”并访问外部作用域中的变量,即使该作用域已执行完毕,就会发生这种情况。
闭包是许多 JavaScript 模式的基础,包括回调、事件处理程序和模块。
来源:scope-closures/ch7.md14-27 scope-closures/ch7.md67-105 scope-closures/ch7.md126-131
闭包在 JavaScript 中被广泛用于:
来源:scope-closures/ch7.md108-126 scope-closures/ch7.md152-175 scope-closures/ch7.md296-336
模块模式是一种基于闭包的设计模式,它提供了一种封装私有实现细节,同时公开公共 API 的方法。
来源: scope-closures/ch8.md10-22 scope-closures/ch8.md98-149
JavaScript has evolved several formats for implementing modules
来源: scope-closures/ch8.md191-201 scope-closures/ch8.md202-281 scope-closures/ch8.md282-306
Following the "Principle of Least Exposure" (POLE) is a key best practice for working with scope in JavaScript. This means limiting the accessibility of variables to only the parts of the code that need them.
let/const for variables needed only within specific blocks来源: scope-closures/ch6.md8-39 scope-closures/ch6.md177-189 scope-closures/ch6.md217-246
The global scope serves several important purposes in JavaScript programs
Object, Array, and undefined are defineddocument in browsers or process in Node.jsHowever, the global scope behaves differently across environments
Understanding these environment differences is crucial for writing portable JavaScript.
来源: scope-closures/ch4.md12-114 scope-closures/ch4.md122-156 scope-closures/ch4.md278-306
Scope and closure are fundamental concepts in JavaScript that control variable visibility and lifetime. Through lexical scope, JavaScript determines at compile time which variables are accessible where. Closure extends this mechanism, allowing functions to maintain access to their lexical scope even when executed elsewhere.
Understanding these concepts enables you to
The principles covered in this document form the foundation of the first pillar of JavaScript: the scope and closure system.