1.词法作用域
就是定义在词法阶段的作用域,作用域查找会在找到第一个匹配的标识符时停止。在多层的嵌套作用域中可以定义同名的标识 符,这叫作“遮蔽效应”(内部的标识符“遮蔽”了外部的标识符)。抛开遮蔽效应,作用域查找始终从 运行时所处的最内部作用域开始,逐级向外或者说向上进行,直到遇见第一个匹配的标识符为止。
全局变量会自动成为全局对象(比如浏览器中的window对象)的属性,因此可以不直接通 过全局对象的词法名称,而是间接地通过对全局对象属性的引用来对其进行访问。如:window.a通过这种技术可以访问那些被同名变量所遮蔽的全局变量。但非全局的变量如果被遮蔽了,无 论如何都无法被访问到。无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定。
2.欺骗词法
function foo(str,a){
eval(str);
console.log(a,b);
}
var b=2;
foo('var b=3;',1);//1,3
eval(...)经常被用来执行动态创建的代码。
调用中的‘var b=3;’,这段代码会被当做本来就在那里一样来处理,由于那段代码声明了 一个新的变量b,因此它对已经存在的foo(..)的词法作用域进行了修改。事实上,和前面提到的原 理一样,这段代码实际上在foo(..)内部创建了一个变量b,并遮蔽了外部(全局)作用域中的同名 变量。
当console.log(..)被执行时,会在foo(..)的内部同时找到a和b,但是永远也无法找到外部的b。因 此会输出“1,3”而不是正常情况下会输出的“1, 2”。
but ....
function foo(str){
'use strict';
eval(str);
console.log(a);//a is not defined
}
foo('var a=2');
在严格模式下,它就挣脱不了了。
with()通常被当作重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身。
var obj = {
a:1,
b:2,
c:3
};
//这样重复比较单调
obj.a = 2;
obj.b = 3;
obj.c = 4;
//快捷方式
with(obj){
a = 2;
b = 3;
c = 4;
}
function foo(obj){
with(obj){
a = 2;
}
}
var o1={
a:3;
}
var o2={
b:3;
}
foo(o1);
console.log(o1.a)//2
foo(o2);
console.log(a2.a);//undefined
console.log(a);//2 a被泄露到全局作用域上了
尽管with块可以将一个对象处理为词法作用域,但是这个块内部正常的var声明并不会被 限制在这个块的作用域中,而是被添加到with所处的函数作用域中。
o2的作用域、foo(..)的作用域和全局作用域中都没有找到标识符a,因此当a =2执行时,自动创建 了一个全局变量(因为是非严格模式)。
词法作用域意味着作用域是由书写代码时函数声明的位置来决定的。编译的词法分析阶段基本能 够知道全部标识符在哪里以及是如何声明的,从而能够预测在执行过程中如何对它们进行查找。