发布于 2026-01-06 0 阅读
0

范围 - JavaScript Concepts DEV 的全球展示挑战赛,由 Mux 呈现:展示你的项目!

范围 - JavaScript 概念

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

这是我尝试解释33 个 JS 概念系列文章的一部分

原文发表于我的博客,并附有互动示例。

这部分对应于范围。

范围

变量的作用域是什么?

在编程中,作用域代表着变量的可见性和使用范围。出于显而易见的原因(我们稍后会详细讨论),我们不能允许程序中的所有变量都是全局变量。作用域限制了变量的使用范围和边界。

为什么要对变量进行作用域划分?

1. 防碰撞

我们不能声明两个同名变量,否则会引发引用错误。

var length = 1;
// some operation going on, after sometime you think
var length = 1; // Nope!`}

但是我们绝对不能将同一个变量用于不同的目的。如果没有界限,阅读代码的人就会感到困惑。当团队人数众多时,这个问题会更加严重。谁又能知道其他人是否已经声明过这个变​​量呢?

明确界定变量的访问范围,有助于项目中所有开发人员避免冲突。

2. 垃圾收集

当我们不再使用一个变量时,我们希望该变量及其存储的数据能够被垃圾回收。在动态语言中,我们期望这个过程能够自动完成。但是,如果我们没有对变量的访问范围进行任何限制,编译器就无法判断何时需要进行垃圾回收,除非是在代码的最后阶段。

明确定义变量的访问范围,可以告诉编译器,在此作用域结束时,可以安全地对这些变量进行垃圾回收。

3. 所有权

如果所有变量都是全局变量,那就意味着任何人都可以更改它们。

例如,在某个维护项目中,我需要创建一个自定义事件,以便将事件广播到应用程序的其他部分。但无论我怎么尝试,都无法触发这个自定义事件。原因是其他人已经在全局窗口上声明了一个名为 `window` 的类CustomEvent(他们想自定义这个类,那就随他们便吧!),并且覆盖了我的定义。更糟糕的是,创建这个类的那个人(git blame)甚至不知道JavaScript 中存在Event一个名为 `window` 的函数。CustomEvent

想象一下,在运行时,你的变量随机发生这种情况。如果我们对编写的变量和函数拥有某种所有权,就会发生这种情况。

JavaScript 有两种作用域:

  1. 块范围
  2. 功能范围

我们先来谈谈函数作用域。

功能范围

函数作用域是指任何已声明的变量都可以在函数内部访问。这种特性在 JavaScript 中由来已久,并被广泛使用。

function action() {
  var a = 2;
  ... // actions
  // a can be accessed anywhere in this function.
}

吊装

无论你在哪里声明一个变量,JavaScript 都会将其提升到当前作用域的顶部。但为了语义上的准确性,我们这里要明确一点:JavaScript 并不会实际移动变量,你的代码完全保持不变。编译器只是在其当前作用域中选取变量,并在编译时为其赋值undefined

function init() {
  console.log(a);
  var a = 1;
}

init();

这就是在声明变量之前访问该变量并获取undefined其结果的方法。

你不必害怕函数提升。事实上,函数提升恰恰能帮助你以任意顺序声明函数。因为编译器无论如何都会将其提升到代码顶部,所以声明顺序无关紧要。但是,对于使用 `$($)` 声明的变量var,编写简洁精确的函数至关重要。

另外,请注意,只有声明会被提升,如果在初始化之前使用,它们将不会有任何值。

块范围

从 ES6 开始,这种做法被广泛应用。代码块指的是{}程序中的一个代码块。

块作用域是指在代码块中定义的变量{}只能在该代码块内使用。可以使用 `@ let` 或 `@`将变量声明到块作用域中const

请注意,函数也构成块作用域。

function action(limit) {
  const a = 10; // a can only be used inside this function
  if (a < limit) {
    const b = a + 2; // b can only be used inside this if block, a can also be used here as this block is inside the execution context of a's block
    return b;
  }
}

吊装

变量提升会在块作用域中发生吗?会的。但是如果我们在变量实际声明之前尝试使用它,就会出现错误ReferenceError

这完全说不通。如果它们被提升,难道不应该是未定义的吗?

为了提前解答这个问题,JavaScript 规范定义了一个叫做临时死区 (TDZ) 的概念。它区分了内存中的变量声明(记住:变量提升只是编译器将变量声明放入内存中)和代码中的实际声明。当访问 TDZ 中的块级作用域变量时,会抛出一个异常ReferenceError

这会改变我编写代码的方式吗?

的确如此。作用域和闭包是这门语言最核心的基石之一。它能够也应该影响你对这门语言和声明的思考方式。

我漏掉了什么吗?有什么不对劲的地方吗?或者有什么好东西?请在推特上联系我。

文章来源:https://dev.to/boywithsilverwings/scopes-and-closures-javascript-concepts-4dfj