JavaScript的作用域模型采用的是词法作用域
词法阶段
之前说过,词法阶段就是编译器的第一个编译阶段词法化。
那么,简单来说,词法作用域就是定义在词法阶段的作用域。
换句话说,词法作用域就是由你在编写代码的时候把变量和块作用域写在哪里来决定的
(这一点和动态作用域形成对比)
查找
作用域查找,始终从运行时候所处的作用域,逐层向上查找,直到遇见*第一个*匹配的标识符。
无论函数在那里被调用,如何调用,它的词法作用域
欺骗词法作用域
- 既然词法作用域完全由编写时来决定,那我们有什么办法在运行时来'修改'词法作用域呢?
eval()
function fn1(str, b) {
eval(str);
console.log(a, b);
}
var a = 3;
fn1("var a = 1", 2);// 1, 2
eval会把内部的字符串,直接解析成在编译时就写上的代码。
所以相当于fn1内部直接声明了a,
欺骗了词法作用域,不会再去上级作用域查找。
在严格模式下,eval有自己的词法作用域,意味着其中的声明不能改变所在的作用域。
with
var obj = {
a : 1
}
with(obj) {
a = 2;
b = 3;
}
with函数接受一个对象给属性a赋值为2,b属性赋值失败,obj.b会报undefined
with函数做的事情是这样的,把对象obj的词法作用域单独拿出来。
把obj属性当做标识符。
为a赋值的时候,LHS查询到a,正常赋值。
为b赋值的时候,LHS查询不到,在全局作用域下声明了一个b,赋值为3.
所以console.log(obj.b)//undefined
console.log(b)//3
性能
- eval和with未做编译器优化,速度慢。
- 读取字符串作为代码,很危险。