JavaScript作用域深入
编译相关
一般源代码编译步骤:分词/词法分析:代码分解为词法单元 => 解析/语法分析:将词法单元数组构建为抽象语法树 => 代码生成:将抽象语法树转换为可执行代码
两种查询类型:LHS和RHS,赋值号左边和赋值号右边的区别
a = b
对于a来说,编译器执行的是LHS查询,即获取变量a的容器(也就是地址),要写这个变量
对于b,执行RHS查询,获取变量b的值,要读这个变量
不管是LHS查询还是RHS查询,如果在当前作用域没有查到变量,会不断到外层作用域进行查询,直到全局作用域。
RHS直到全局作用域任未查询到的话会抛出ReferenceError
而对于LHS,如果在全局作用域没有找到的话,会在全局作用域中创建一个该变量,所以就会出现这种情况
function foo(a){
console.log(a)
b = a
}
foo(2)
console.log(b) // 打印2 且不会报错
作用域欺骗
eval和with会对作用域产生奇奇怪怪的影响,且会导致引擎无法进行优化,严重影响效率,禁止使用
函数作用域和块作用域
函数表达式、立即执行函数表达式
函数表达式
setTimeout(function(){
// todo
},1000)
立即执行函数表达式
(function(a){
console.log(a);
})(a)
块作用域
三种类型:
- 选择循环流程块,类似for,if块
- try catch块
- {}块
对于 var修饰的变量,实际作用域会跳出当前块作用域(并不会跳出函数作用域),所以才有了let和const,会被限制在块作用域中
function foo(a){
if(a){
var b=2
}
console.log(b) // 能够得到2,因为var定义的b会跳出块作用域
}
foo(true)
console.log(b) //ReferenceError 跳不出函数作用域