词法作用域

作用域有两种模型,一种是词法作用域,一种是动态作用域,而js用的就是词法作用域。词法作用域就是在词法化时的作用域,变量及代码块写在哪就是在哪,当词法分析器处理代码时不会改变作用域。


一·词法阶段

词法阶段就是词法分析器处理代码的阶段,下面将涉及引擎怎么找想要的变量,还有作用域嵌套中的隐蔽效应,就拿下面一段嵌套作用域来详细讲

1
2
3
4
5
6
7
8
function foo(a){
var b = a*2;
function bar (c){
console.log(a,b,c);
}
bar(b*3);
}
foo(2) //2 4 12

首先划分一下作用域

全局作用域>包含foo所创建的作用域>包含着bar所创建的作用域

这下我们就可以看到嵌套关系

还需要搞清楚的一点就是引擎在查找的时候是逐级向外的,也就是说,他会从最里层开始查找

回到这个例子,引擎来到最里层,也就是包含着bar所创建的作用域,他需要找到a b c这三个变量,可是在这一层他只能找到c,所以他就要去上一级接着找,这时他就找到了a和b

正是引擎的这种查找方式使遮蔽效应诞生了,现在再强化一下查找方式,引擎从最里面一层开始逐层往外查找且找到第一个匹配的就停止查找。也就是说如果我的嵌套作用域里用相同名字的变量,里面的会遮蔽掉外层的变量。

二·欺骗词法

所谓欺骗词法就是修改词法作用域,一种是eval(),一种是with

  1. eval()
    eval()接受一个字符串参数,当执行动态创建的代码时有时可能需要用到拼接,它是在运行期修改书写期。以下面一段代码为例
1
2
3
4
5
6
function foo(str,a){
eval(str);//欺骗
console.log(a,b);
}
var b = 2;
foo("var b = 3",1)

eval()是在运行时修改词法作用域的,可以理解成eval写在哪,其中包含的代码就在哪,这样来看,引擎在内部就找到了b就不需要再去外层寻找了。输出结果就是1,3而不是1,2。

需要注意的是,在严格模式下,引擎是不会放过他的,因此会失效,无法修改词法作用域

  1. with

with通常被当做重复引用同一对象的多个属性的快捷方式

foo(obj){
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    with(obj){
a=2;
}
}
var o1 = {
a:3
};
var o2 = {
b:3
}
foo(o1);
console.log(o1.a);//2
foo(o2);
console.log(o2.a);//undefined
console.log(a);//2,泄漏到全局作用域

这里着重说一下为什么会泄漏到全局作用域,因为o2没有a,所以在非严格模式下会进行LHS引用,这样就泄漏了

最后需要说一下,欺骗词法作用域了解就好,尽量不要使用,因为会降低性能。而且受严格模式限制。

-------------本文结束感谢您的阅读-------------