在JavaScript中,我们可以将作用域定义为一套规则,这套规则用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找,解决变量到底取什么值的问题。JS中作用域有:全局作用域、函数作用域和eval作用域,因为eval不推荐使用,因此也很少提及eval作用域。with作用域可以看作是全局作用域或函数作用域中的一个子作用域。本文谈谈with作用域的原理机制。
1、先看个DEMO
DEMO来自“深入理解JavaScript系列(10):JavaScript核心(晋级高手必读篇)”
Object.prototype.x = 10;
var w = 20;
var y = 30;
// 例如,全局上下文的变量对象是从"Object.prototype"继承到的
// 所以我们可以得到“没有声明的全局变量”
// 因为可以从原型链中获取
console.log(x); // 10
(function foo() {
// "foo" 是局部变量
var w = 40;
var x = 100;
// "x" 可以从"Object.prototype"得到,注意值是10哦
// 因为{z: 50}是从它那里继承的
with ({z: 50}) {
console.log(w, x, y , z); // 40, 10, 30, 50
}
// x又可以从foo的上下文中得到了,注意这次值又回到了100哦
// "w" 也是局部变量
console.log(x, w); // 100, 40
// 在浏览器里
// 我们可以通过如下语句来得到全局的w值
console.log(window.w); // 20
})();
执行该代码,我们可以得到输出:
10
40 10 30 50
100 40
20
2、代码说明:
- console.log(x); // 10
全局上下文变量对象:globalVO = {w:20, y:30, __ proto __:Object.prototyp}
Object.prototype = {x:10, …}
从原型链的角度来说:当前globalVO没有x标识符,则找globalVO. __ proto __,找到了有x标识符,值为10,所以console.log(x)输出10 - with作用域
with ({z: 50}) {
console.log(w, x, y , z); // 40, 10, 30, 50
}
with作用域链,会从2个纬度来搜寻:
1)对象原型链(优先)
2)上下文作用域链var withZ = {z: 50}; console.log("withZ.x = ", withZ.x); //withZ.x = 10 with (withZ) { console.log(w, x, y , z); // 40, 10, 30, 50 }
为了大家更加易于理解,代码做了简单修改,如上所示。我们可以明显看出:
withZ实际上等同{x: 10, z:50},则:
w: withZ原型链上没有,查上下文作用域链,with作用域外层有,故w=40
x: withZ原型链上有,直接取原型的x值,故x=10
y: withZ原型链上没有,查上下文作用域链,全局作用域有,故y=30
z: withZ上有,取当前对象值,故z=50
因此,console.log(w, x, y , z); // 40, 10, 30, 50
3、总结
with作用域有2个搜寻纬度:
1)with对象的原型链,优先搜寻
2)上下文作用域链