理解JS中的作用域

Js作用域在开发中无处不在,可以说没有js作用域就没有了可以正确被访问变量的可能。简单来说js作用域用于控制变量和函数的可访问范围,一旦超出这个范围,变量和函数便不能被访问,作用域分为几类全局作用域
,函数作用域
,块级作用域
,词法作用域
。
作用域看着简单,实际上还是有一些可以深挖的点的,比如作用域链
,闭包和作用域的联系
,如何避免全局变量的污染
,使用 IIFE(立即调用函数表达式)来创建一个私有作用域
,如何在 JavaScript 中实现私有变量和方法,什么是“动态作用域”(JS中没有动态作用域,这里只是举一个例子),并讨论 JavaScript 是如何使用词法作用域的等等。
进入主题
全局作用域,函数作用域,块级作用域等基础概念只做简单的介绍,不做过多涉及。
全局作用域
在 JavaScript 中,任何在函数外部声明的变量都是全局变量,属于全局作用域。全局变量可以在任何地方访问,包括函数内部。
1 | var globalVar = "I am global"; |
函数作用域
每当一个函数被创建时,都会创建一个新的作用域。函数内部声明的变量只能在该函数内部访问,外部无法访问。
1 | function testFunctionScope() { |
块级作用域
ES6 引入了 let 和 const 关键字,它们提供了块级作用域。块级作用域是指在 {} 代码块内声明的变量只在该代码块内有效。
1 | if (true) { |
作用域链
作用域链是一个由多个作用域组成的结构,形成了一个链式的访问路径。每当一个函数被调用时,JavaScript 会创建一个新的执行上下文,并将其作用域链与当前作用域链相结合。作用域链的结构确保了在函数内部可以访问到其外部的变量
。
示例:
1 | var globalVar = "I am a global variable"; |
闭包与作用域链
闭包是 JavaScript 中一个强大的特性,它允许函数访问其外部作用域的变量,即使外部函数已经返回。闭包的存在使得作用域链得以延续,确保了对外部变量的访问。
1 | function makeCounter() { |
在这个例子中,makeCounter 返回了一个函数,这个函数形成了一个闭包,保持了对 count 变量的引用。即使 makeCounter 执行完毕,count 依然可以被访问。
作用域链的存在对 JavaScript 的性能和内存管理有重要影响:
- 内存管理:闭包会保持对外部变量的引用,这可能导致内存泄漏。如果不恰当地使用闭包,可能会导致不再需要的变量无法被垃圾回收。
- 变量查找:作用域链的查找过程会影响变量访问的
速度
。局部变量的查找速度通常比全局变量快,因为局部变量在当前作用域中直接可用,而全局变量则需要逐层查找。
使用 IIFE(立即调用函数表达式)来创建一个私有作用域
立即调用函数表达式(IIFE, Immediately Invoked Function Expression)是一种常用的 JavaScript 编程模式,主要用于创建一个私有作用域,从而避免变量污染全局作用域。IIFE 允许你在函数内部定义变量和函数,这些变量和函数不会暴露到外部作用域,能够有效地封装代码。
什么是IIFE
IIFE 是一个在定义后立即执行的函数。它的基本语法如下:
1 | (function() { |
作用
通过 IIFE,你可以创建一个私有作用域,确保在 IIFE 内部定义的变量不会被外部访问。这对于封装逻辑
、避免全局命名冲突
以及保护变量不被外部修改
非常有用。
1 | var globalVar = "I am a global variable"; |
应用场景
封装模块:使用 IIFE 可以创建模块化的代码结构,避免全局命名冲突。例如,可以将相关的功能封装在一个 IIFE 中,形成一个模块。
1 | var myModule = (function() { |
动态作用域
动态作用域(Dynamic Scope)是一种变量作用域的管理方式,与静态作用域(或词法作用域)相对。在动态作用域中,变量的作用域是根据程序的调用栈(call stack)来决定的,而不是根据代码的结构来决定的。这意味着一个函数可以访问在其调用链中定义的变量,而不仅仅是它自己定义的变量。
注意:在 JavaScript 中,没有动态作用域(dynamic scope),而是采用了 词法作用域(lexical scope)。
动态作用域的特点
- 调用上下文:
- 在动态作用域中,变量的可见性取决于函数的调用顺序。当一个函数被调用时,它可以访问所有在其调用链中定义的变量。
- 运行时决定:
- 变量的解析是在运行时进行的,而不是在编写代码时。这使得动态作用域的行为在某些情况下可能会更加灵活,但也可能导致不易预测的结果。
- 对嵌套函数的影响:
- 如果一个函数在另一个函数内部被调用,内层函数可以访问外层函数中的变量,但这取决于调用的上下文。
1
2
3
4
5
6
7
8
9
10
11function outer() {
var x = 10;
function inner() {
print(x); // 这里的 x 是从 outer 函数中获取的
}
inner(); // 调用 inner 函数
}
outer(); // 输出 10
- 如果一个函数在另一个函数内部被调用,内层函数可以访问外层函数中的变量,但这取决于调用的上下文。
- Title: 理解JS中的作用域
- Author: tianyi
- Created at : 2025-05-05 09:53:02
- Updated at : 2025-05-05 10:07:06
- Link: https://github.com/ztygod/2025/05/05/理解JS中的作用域/
- License: This work is licensed under CC BY-NC-SA 4.0.