本 Wiki 页面解释了 JavaScript 中函数作用域和块作用域之间的区别,以及何时以及如何使用每种作用域。理解这些概念对于编写具有适当变量封装的可维护代码至关重要。本节重点比较使用 var 声明的函数级作用域与使用 let 和 const 声明的块级作用域,以及它们对程序设计的影响。
有关整个作用域链以及嵌套作用域如何交互的相关信息,请参阅 作用域链。有关不同变量声明的提升行为的详细信息,请参阅 变量(不那么)秘密的生命周期。
在 JavaScript 中,函数传统上是作用域创建的主要单元。当我们使用 var 声明变量或创建函数声明时,这些声明会绑定到包含它的函数作用域,无论它们在函数内的何处出现。
函数作用域意味着使用 var 声明的变量在整个函数内都可访问,不受函数内任何块边界的限制。这种行为有时会导致意外的变量泄露。
来源: scope-closures/ch3.md1-149 scope-closures/ch3.md1-61
来源: scope-closures/ch3.md1-61 scope-closures/ch2.md1-80
随着 ES6 中 let 和 const 的引入,JavaScript 获得了真正的块作用域能力。块(用 { } 花括号表示)现在可以使用这些声明作为作用域边界,将变量的可见性限制在仅该块内。
一个关键的区别是,块作用域变量在运行时执行其声明之前不会被创建(与 var 变量不同,后者会被提升并用 undefined 初始化)。这创造了所谓的“暂时性死区”(TDZ)——一个变量存在但无法在其声明之前访问的时期。
来源: scope-closures/ch6.md216-306 scope-closures/ch5.md1-150
来源: scope-closures/ch6.md216-270 scope-closures/ch2.md30-70
理解何时使用每种作用域至关重要。以下是关键区别的比较:
| 功能 | 函数作用域(var) | 块作用域(let/const) |
|---|---|---|
| 作用域边界 | 受函数边界限制 | 受任何 { } 块边界限制 |
| 提升 (Hoisting) | 被提升并用 undefined 初始化 | 被提升但未初始化(TDZ) |
| 重新声明 | 在同一作用域内允许 | 在同一作用域内不允许 |
| 生命周期 | 整个函数都存在 | 仅在其声明的块内存在 |
| 全局绑定 | 在全局对象上创建属性 | 不在全局对象上创建属性 |
| 可见性 | 在整个函数中可用 | 仅在块内声明之后可用 |
来源: scope-closures/ch5.md30-250 scope-closures/ch3.md180-260 scope-closures/ch6.md216-306
来源: scope-closures/ch2.md1-80 scope-closures/ch3.md1-100 scope-closures/ch6.md216-306
在实际代码中,混合方法通常是最有效的。“最小暴露原则”(POLE)表明,变量应在尽可能小的作用域中声明,以尽量减少意外的交互并提高代码的可维护性。
var)用于let/const)用于来源: scope-closures/ch6.md1-80 scope-closures/apA.md590-661
一个特殊的考虑因素是函数声明在块内的行为。块内的函数声明在 JavaScript 环境中的处理方式历来不一致。
在严格模式下(ES 模块中的默认设置),块内的函数声明作用于该块。但是,在非严格模式下,它们可能会被提升到包含的函数作用域甚至全局作用域,具体取决于 JS 引擎。
为了在所有环境中保持一致性,建议在块内使用带 let 或 const 的函数表达式
来源: scope-closures/ch6.md389-430 scope-closures/ch5.md41-60
在 ES6 引入块作用域之前,立即调用函数表达式(IIFE)常被用来创建一个新作用域。
有了块作用域,我们现在可以更简洁地实现类似的封装。
主要区别在于函数会创建自己的作用域边界,这会影响 return、this、break 和 continue 语句的行为。块没有这些相同的边界效应,这使得它们在某些情况下更具灵活性。
来源: scope-closures/ch6.md180-215 scope-closures/ch6.md307-380
来源: scope-closures/ch6.md180-215 scope-closures/ch6.md307-380
遮蔽(Shadowing)发生在内层作用域的变量与外层作用域的变量同名时。内层变量会“遮蔽”外层变量,阻止从内层作用域访问它。
遮蔽行为因声明类型而异
let 可以遮蔽 varvar 不能遮蔽 let(会导致 SyntaxError),如果它们之间没有函数边界。来源: scope-closures/ch3.md206-260
遵循“最小暴露原则”(POLE),您应该
var 和 let/const,为每个任务选择合适的工具var 用于函数范围的变量let/const 用于块作用域的变量理解函数作用域和块作用域的区别,可以帮助您编写更易于维护的代码,并减少因意外变量访问或冲突而导致的错误。