JS 作用域

168 阅读4分钟

JavaScript 作用域

本文用于个人学习记录,参考了其它文章,非官方解释,如有错漏,欢迎指正

JS 作用域

在 JavaScript 中,作用域(Scope) 是一个非常重要的概念。它决定了变量、函数和对象的可访问性和生命周期

JavaScript 采用了 词法作用域(Lexical Scope),也就是 静态作用域,这意味着变量的作用域在语法解析时就已经确定了。换句话说,一个变量的作用域取决于它在哪里声明,而不是它在哪里被使用

JS 作用域链

作用域链是 JavaScript 中用于查找变量的一种机制。它由当前作用域和所有父级作用域的变量对象组成。当访问一个变量时,JavaScript 引擎会首先在当前作用域的变量对象中查找,如果找不到,则沿着作用域链向上查找,直到找到变量或者到达全局作用域

在浏览器的 devtool 或者 vscode 中,我们可以通过打断点实时追踪代码执行过程中的作用域链变化情况

JS 作用域的类别

1.Global

在浏览器环境下,Global 作用域即是 window 对象

global-scope.png 在 Node 环境下,Global 作用域则是 global 对象

node-global-scope.png

2. Script

只存在与浏览器环境下的作用域,一个 <script> 元素对应一个 Script 作用域,在顶层使用 let, const 声明的变量存放于 Script 作用域中

script-scope.png

3. Module

使用 ESM 的情况下特有的作用域,所有在顶层声明的变量和函数都存放于 Module 作用域中

module-scope.png

4. Local

Local 作用域即局部作用域,在 JavaScript 中,Local 总是函数中的作用域,所以也可以称为函数作用域

local-scope.png

Node 环境下的模块,本质是一个特殊的函数,它包含 exports、require、module、__dirname、__filename 这五个参数

所以 Node 环境下的模块作用域,本质是一个特殊的 Local 作用域,而非 Module 作用域

node-module-scope.png

5. Block

ES6 引入块语句后,每个块语句都会生成一个单独的 Block 作用域

if,while,for 等语句的语句块也会生成 Block 作用域

在这些语句块中,使用 let 和 const 声明的变量会存放在对应的 Block 作用域中

block-scope.png

6. Catch block

一个 catch 语句除了生成一个 Block 作用域,还会生成一个特殊的 Catch block 作用域,Catch block 作用域中只存放了一个错误对象

catch-block-scope.png

7. With block

一个 with 语句除了生成一个 Block 作用域,还会生成一个特殊的 With block 作用域,Catch block 作用域即是在 with 语句中传入的对象

with-block-scope.png

8. Eval

eval() 函数会生成一个单独的作用域,在 eval 的代码里使用 let 或 const 声明的变量都在这个作用域里

eval-scope.png

9. Closure

Closure,即闭包,是 JavaScript 中一个非常重要的概念。关于闭包的概念,有很多不同版本的定义,这里例举一些比较权威的定义:

MDN的定义如下:

闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建

《JavaScript 高级程序设计》第四版中是这样定义的:

闭包指的是引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的

《JavaScript 权威指南》第六版中则是这样:

从技术的角度讲,所有的 JavaScript 函数都是闭包,它们都是对象,它们都关联到作用域链

emm,老实说有点难以理解为什么有这么多不同的解释...

自己 debugger 看看:

closure-scope.png

通过调试,我观察的结果是:

假设存在一个函数 f,当其内部函数需要访问 f 中的变量时,JS 引擎便会为 f 创建一个与之同名的闭包,里面存放了所有内部函数会用到的变量,内部函数执行时,通过该闭包来访问 f 中的变量

内部函数可以有多个,但共享同一个闭包

直到所有需要访问 f 中的变量的内部函数销毁之前,f 对应的闭包都会一直存在,即便 f 本身已经销毁

相关参考来源

  1. JS 的 9 种作用域,你能说出几种? - 掘金 (juejin.cn)
  2. 闭包 - JavaScript | MDN (mozilla.org)