一、何为作用域
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)