菜单

变量与作用域

相关源文件

本页面解释了 JavaScript 变量在声明、初始化以及在不同代码块中的可见性方面的工作原理。它涵盖了三个主要的变量声明关键字(varletconst)、提升(hoisting)如何影响它们以及暂时性死区(Temporal Dead Zone)的概念。

有关闭包及其与作用域交互的信息,请参阅函数和 this 上下文

JavaScript 变量概述

JavaScript 提供了三种声明变量的方式,每种方式在作用域、提升和可变性方面都有独特的行为。

关键词范围提升 (Hoisting)可重新赋值可重复声明暂时死区
var函数/全局提升,值为 undefined
let模块提升,未初始化
const模块提升,未初始化

来源: README.md54-82 README.md727-750

JavaScript 中的变量作用域

JavaScript 有几种不同类型的作用域,它们决定了变量的可见性和生命周期。

来源: README.md86-112 README.md1635-1667

全局作用域

在任何函数或代码块之外声明的变量都处于全局作用域中,可以在程序的任何地方访问。有三种方法可以创建全局变量:

  1. 在任何函数或代码块外部声明变量
  2. 为未声明的变量赋值(不使用 varletconst
  3. 使用全局对象属性(如浏览器中的 window.variableName

在未声明的情况下创建全局变量(隐式全局变量)时,它们会成为全局对象的属性,并且可以被删除,这与正确声明的变量不同。

来源: README.md804-822 README.md1635-1667

函数作用域

在函数内部声明的变量只能在该函数内部访问。var 关键字创建的是函数作用域变量。

函数作用域在函数内部声明的变量周围创建了一个保护性“气泡”,防止它们在外部被访问。这对于创建私有变量很有用。

来源: README.md54-82

块级作用域

ES6 引入了块级作用域,将变量的可见性限制在它们定义的块内(大括号 {} 之间的任何内容)。这包括循环、条件语句和独立的块。letconst 关键字创建的是块级作用域变量。

块级作用域在循环中尤为重要。在循环中使用 let 会为每次迭代创建一个新变量。

来源: README.md86-112 README.md1635-1667

提升 (Hoisting)

JavaScript 在编译阶段(代码执行之前)将变量和函数声明“提升”到其包含作用域的顶部。这种行为因声明类型而异。

请看以下示例,它演示了提升:

来源: README.md54-82 README.md1635-1667

暂时性死区 (TDZ)

暂时性死区是指在进入使用 letconst 声明变量的作用域到实际声明行之间的这段时间。在此区域内,访问变量会导致 ReferenceError

此行为在 README.md54-82 中有演示,其中在初始化 let 变量之前尝试访问它会导致 ReferenceError

来源: README.md54-82

作用域的实际应用

循环变量和闭包

理解作用域最重要的实际应用之一是处理闭包中的循环变量。这在问题 2 中有所演示。

使用 var 时,整个循环只创建一个变量 i,并且在超时执行时,i 已经递增到 3。使用 let 时,每次迭代都会创建一个新的 i,从而在闭包中保留其值。

来源: README.md86-112

意外的全局变量

如果不使用 varletconst,赋值操作会在全局对象上创建属性。

在严格模式下,这会抛出 ReferenceError,从而防止意外创建全局变量。

来源: README.md627-654 README.md1635-1667

重复声明行为

使用 var 声明的变量可以在同一作用域内多次重复声明,而 letconst 则不能。

来源: README.md727-750

变量使用的最佳实践

  1. 默认情况下对不需要重新赋值的变量使用 const,以减少因意外重新赋值导致的错误。
  2. 当变量值需要更改时使用 let,以清晰地表明变量的值将发生变化。
  3. 在现代代码中避免使用 var,因为其函数作用域和提升行为可能导致难以察觉的错误。
  4. 始终使用适当的关键字声明变量,而不是依赖隐式全局变量。
  5. 使用严格模式'use strict';)来防止意外创建全局变量。
  6. 将变量保持在尽可能窄的作用域内,以最大程度地减少意外的副作用。

来源: README.md54-82 README.md86-112 README.md627-654 README.md1635-1667

与 JavaScript 执行上下文的关系

变量声明与 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 也不会添加到全局对象。

与传统的脚本加载相比,这提供了更好的封装性。

来源: README.md1643-1763

通过理解这些作用域机制,开发人员可以编写更易于维护且更少意外行为的代码。