作用域是什么
- 引擎 负责javaScript程序的编译及执行过程。
- 编译器 语法分析及代码生成(类似编译原理这门课程的内容)
- 作用域 收集并维护由所有声明的变量组成的一系列查询,确定当前执行的代码对这些变量的访问权限。
LHS 变量出现在赋值操作的左侧。a=2,为=2这个赋值找到目标。
在非严格模式下,如果在顶层中也无法找到目标变量(LHS查询失败时),全局作用域会创建一个具有该名称的变量并返回给引擎。
严格模式下,禁止自动会隐式创建全局变量,LHS查询失败时,引擎抛出ReferenceError。
RHS 变量出现在赋值操作的右侧,理解为得到这个值。console.log(a)
RHS 查询在所有嵌套的作用域里遍寻不到所需的变量,引擎抛出ReferenceError。
function foo(a){
console.log(a);
}
foo(2)
隐含了a=2这个LHS操作(调用foo(2)的时候)
javascript编译步骤
var a=2;
- 看到var a,编译器询问是否有同名变量在同一作用域的集合中。若有,忽略声明。若无,在当前作用域集合中声明一个变量,命名为a。
- 进行a=2这个赋值操作。引擎询问作用域集合中是否存在一个叫做a的变量。若有,引擎使用这个变量,若无,引擎继续查找这个变量。
作用域嵌套:一个块或函数嵌套在另一块或函数中。先在当前作用域中找某个变量,找不到会在外层嵌套的作用域中继续查找,找到||抵达全局作用域(最外层作用域)时停止。
function foo(a){
console.log(a+b);
}
var b=2;
foo(2);
对b的RHS引用无法在函数foo内部完成,可以在上一级作用域(全局作用域)完成。
词法作用域
词法作用域是定义在词法阶段的作用域。
欺骗词法
在运行时修改词法作用域
1.eval
function foo(str,a){
eval(str);
console.log(a,b);
}
var b=2;
foo("var b=3;",1);//1,3
var b=3声明了一个新的变量b,对已经存在的foo(..)词法作用域进行了修改。可以理解为在foo内部创建了一个b,覆盖了外部的b。
严格模式下,其他声明无法修改所在的作用域,Reference Error,a is not defined。
2.with
var obj={
a:1,
b:2,
c:3
};
obj.a=2;
obj.b=2;
obj.c=3;
with(obj){
a=3;
b=4;
c=5;
}
function foo(obj){
with(obj){
a=2
}
}
var o2={
b=3
}
foo(o2)
console.log(a)//2(a被泄露到全局作用域上)
with可以减少简单重复的工作,但可能把变量泄露到全局作用域上,例如上面,将o2传给with,o2没有标识符a,自动创建了一个全局变量(非严格模式下)。
eval函数修改其所处的词法作用域。
with为传递给它的对象创建了一个新的词法作用域。
eval和with的副作用
引擎无法在编译时对作用域查找进行优化,因为无法知道在词法分析阶段明确直达eval(...)会接收什么代码,这些代码对词法作用域的影响,也无法知道with接收到的对象是什么。