范围 - 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 有两种作用域:
- 块范围
- 功能范围
我们先来谈谈函数作用域。
功能范围
函数作用域是指任何已声明的变量都可以在函数内部访问。这种特性在 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