本页面解释了 JavaScript 变量在声明、初始化以及在不同代码块中的可见性方面的工作原理。它涵盖了三个主要的变量声明关键字(var、let 和 const)、提升(hoisting)如何影响它们以及暂时性死区(Temporal Dead Zone)的概念。
有关闭包及其与作用域交互的信息,请参阅函数和 this 上下文。
JavaScript 提供了三种声明变量的方式,每种方式在作用域、提升和可变性方面都有独特的行为。
| 关键词 | 范围 | 提升 (Hoisting) | 可重新赋值 | 可重复声明 | 暂时死区 |
|---|---|---|---|---|---|
var | 函数/全局 | 提升,值为 undefined | 是 | 是 | 否 |
let | 模块 | 提升,未初始化 | 是 | 否 | 是 |
const | 模块 | 提升,未初始化 | 否 | 否 | 是 |
来源: README.md54-82 README.md727-750
JavaScript 有几种不同类型的作用域,它们决定了变量的可见性和生命周期。
来源: README.md86-112 README.md1635-1667
在任何函数或代码块之外声明的变量都处于全局作用域中,可以在程序的任何地方访问。有三种方法可以创建全局变量:
var、let 或 const)window.variableName)在未声明的情况下创建全局变量(隐式全局变量)时,它们会成为全局对象的属性,并且可以被删除,这与正确声明的变量不同。
来源: README.md804-822 README.md1635-1667
在函数内部声明的变量只能在该函数内部访问。var 关键字创建的是函数作用域变量。
函数作用域在函数内部声明的变量周围创建了一个保护性“气泡”,防止它们在外部被访问。这对于创建私有变量很有用。
来源: README.md54-82
ES6 引入了块级作用域,将变量的可见性限制在它们定义的块内(大括号 {} 之间的任何内容)。这包括循环、条件语句和独立的块。let 和 const 关键字创建的是块级作用域变量。
块级作用域在循环中尤为重要。在循环中使用 let 会为每次迭代创建一个新变量。
来源: README.md86-112 README.md1635-1667
JavaScript 在编译阶段(代码执行之前)将变量和函数声明“提升”到其包含作用域的顶部。这种行为因声明类型而异。
请看以下示例,它演示了提升:
来源: README.md54-82 README.md1635-1667
暂时性死区是指在进入使用 let 或 const 声明变量的作用域到实际声明行之间的这段时间。在此区域内,访问变量会导致 ReferenceError。
此行为在 README.md54-82 中有演示,其中在初始化 let 变量之前尝试访问它会导致 ReferenceError。
来源: README.md54-82
理解作用域最重要的实际应用之一是处理闭包中的循环变量。这在问题 2 中有所演示。
使用 var 时,整个循环只创建一个变量 i,并且在超时执行时,i 已经递增到 3。使用 let 时,每次迭代都会创建一个新的 i,从而在闭包中保留其值。
来源: README.md86-112
如果不使用 var、let 或 const,赋值操作会在全局对象上创建属性。
在严格模式下,这会抛出 ReferenceError,从而防止意外创建全局变量。
来源: README.md627-654 README.md1635-1667
使用 var 声明的变量可以在同一作用域内多次重复声明,而 let 和 const 则不能。
来源: README.md727-750
const,以减少因意外重新赋值导致的错误。let,以清晰地表明变量的值将发生变化。var,因为其函数作用域和提升行为可能导致难以察觉的错误。'use strict';)来防止意外创建全局变量。来源: README.md54-82 README.md86-112 README.md627-654 README.md1635-1667
变量声明与 JavaScript 的执行上下文模型紧密相关,该模型包括创建和执行阶段。
全局执行上下文创建全局对象和 this 关键字,它们可以从任何作用域访问。这解释了为什么未声明的变量会成为全局对象的属性。
来源: README.md804-822 README.md54-82 README.md1635-1667
this 关键字和箭头函数this 的行为受所用函数类型的影响。
箭头函数没有自己的 this 绑定——它们在定义时从周围的作用域继承 this。
来源: README.md116-146
在 ES6+ 中,块内的函数声明也具有块级作用域(在严格模式下)。
当依赖条件块中的函数声明时,这可能导致难以察觉的错误。
来源: README.md54-82 README.md86-112
在 ES 模块中,变量默认是模块作用域的,即使使用 var 也不会添加到全局对象。
与传统的脚本加载相比,这提供了更好的封装性。
通过理解这些作用域机制,开发人员可以编写更易于维护且更少意外行为的代码。