一、何为作用域
JS 中的作用域,可以理解为一种标识符访问权限的规则。通过这个规则,可以确定变量在当前执行环境内,是否可以访问/操作。
二、作用域的产生
那作用域是如何产生的呢? 具体有什么用处呢?
从写的代码,到编译为电脑可以识别的指令。语言 => 机器指令,大致经历了以下阶段:
- 对源代码进行语法分析,得到词法单元。举例:
var a = 1;分析后,变为var、a、=、1、; - 对上一步产生的词法单元,再进行语法分析处理,构建抽象语法树(AST),类似于伪代码
- 根据 AST 生成,电脑可以执行的机器指令
这个过程中,词法分析又引擎负责处理。而语法分析、代码生成则由编译器处理。在语法分析阶段,作用域 则提供了所有标识符的访问、赋值权限。
作用域在产生在代码书写的位置。变量的操作权限由当前位置开始,逐级往上,直至找到第一个匹配规则。
三、作用域的查找
正如上面所提到的,语法分析时,要对代码中的变量,进行读写。那么就涉及到了赋值 、引用。于是,产生了 2 种查找方式。即:
LHS查询:找到赋值操作的目标(可以理解为赋值)RHS查询:找到赋值操作的源头(可以理解为引用)
举例说明:
var a = 1;console.log(a);
- 找到变量
a,当前作用域不存在变量,于是马上创建一个a。 - 给变量
a赋值,也就是进行LHS查询 - 找到
console对象和log方式 - 进行一次
RHS查询,找到 变量a - 执行语句
四、作用域的修改
作用域的产生,是发生在语法分析阶段。代码编译好之后,还是有可能修改干预修改作用域的。
eval(修改词法作用域)
function foo(str, a) {eval( str ); // 修改了当前作用域console.log( a, b );}var b = 2;foo( "var b = 3;", 1 ); // 1, 3
with(创建全新作用域) ```javascript // 重复引用同一个对象的多个属性 function f(obj) { with (obj) { a = 1; } } var o1 = { a: 0 }; var o2 = { b: 2 };
f(o1); console.log(o1.a); // 1
f(o2); console.log(o2.a); console.log(a); // a 被泄露在了全局作用域 ```
五、作用域的提升
在执行代码之前,处理变量/函数声明。
- 变量提升:提升到当前词法作用域的最前端
- 函数提升:只提升函数声明,不提升函数表达式
六、作用的类型
- 全局作用域在任何位置,都可以访问到变量。常见的全局作用域:
- 程序最外层定义的函数或变量
- 所有未定义,直接赋值的变量
window对象的属性、方法
- 块级作用域:
let和const声明的变量,形成的作用域 - 函数作用域:具名函数、匿名函数、立即执行函数表达式(IIFE)
