词法作用域意味着作用域是由书写代码时函数声明的位置来决定的。嵌套的作用域类似为严格包含的嵌套气泡结构。
作用域查找从运行时所处的最内部作用域开始,逐级向外进行,直至遇见第一个匹配的标识符为止。
这种查找方式将引起“遮蔽效应”,即内部的标识符“遮蔽”了外部的标识符,被遮蔽的标识符除了全局变量
可以通过window.a的方式来访问,其余非全局的变量将无法被访问到。(全局变量会自动转换为window对象的属性)
存在两种机制可以打破这种规则:
eval()
function foo(str,a){
eval(str); //欺骗!
console.log(a , b);
}
var b = 2;
foo("var b = 3;",1); //1,3
在以上的代码块中,调用foo函数时对str传的参数是 var b = 3字符串,eval函数将会将这一段字符串转化为真实的有效代码,因而将原本会打印1,2的打印函数改写成了1,3 ,即原本foo函数作用域中不存在b的定义,在打印时将会查询到全局作用域下的b变量,但eval函数欺骗了作用域,在执行时在函数作用域中强行创建了一个b变量从而遮蔽了全局变量b。
严格模式中eval()有其自己的词法作用域,因而将会引发Reference异常,
with()
with方法可以使对同一个对象的多次访问变得便捷
var obj = {
a:1,
b,2,
c:3
};
//若要对obj中的属性进行修改,则需要逐个访问
obj.a = 2;
obj.b = 3;
obj.c = 4;
//使用with方法
with(obj) {
a = 3;
b = 4;
c = 5;
}
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(o2.a); // undefined
console.log(a); // 2 a被泄露到了全局作用域上了
第一次调用foo函数,对o1内部的a做了改动
第二次调用foo函数,o2内部不存在a属性,于是作用域查询上级作用域即全局作用域,也不存在,于是创建了一个a变量存储2
两次对a的赋值都是LHS查询,第一次找到了盒子于是对盒子里的值进行修改,第二次没有查询到于是创建了这个盒子并赋值。