父作用域中的局部变量会污染到子作用域,当然,可以在子作用域中可以重新声明这个变量来覆盖父作用域的污染。但是即便如此结果依然与干净的作用域不同。重定义变量后变量依然存在,只是值为undefined。干净的作用域中访问不存在的变量应该会抛出异常。
(function(){
var n="次碳酸钴";
(function(){
var n;
alert(n); //undefined
})();
})();
(function(){
var n="次碳酸钴";
delete n;
(function(){
alert(n); //次碳酸钴
})();
})();
(function(){
(function(){
alert(n); //Error: n is not defined
})();
})();
现在我们知道,要保持子作用域是干净的就不能在父作用域中使用局部变量。现在有这样一个需求,父作用域中有两个子作用域,第一个子作用域运行后会返回一个对象,第二个子作用域是干净的。然后需要把第一个子作用域返回对象的各个键作为第二个作用域的局部变量(键的个数和值是不固定的),也就是在一个干净的作用域中导入一组变量。类似下面这个with语句所完成的效果,但是with语句对效率影响巨大,不能使用它。必须找到一种方法来模拟这个with。
(function(){
with(function(){
return {a:"1",b:"2",c:"3"};
}())(function(){
alert(a+b+c); //123
})();
})();
如果不能在父作用域使用局部变量就有点麻烦了。其实,通常变量的指针是储存在栈中的,var和function语句会预执行就是因为需要计算作用域中局部变量指针所占空间的大小,从而给他们分配内存。栈中的东西是不能随意删除的,所以我们无法删除这些变量。但是某些情况下变量也可以放在堆中,比如使用eval执行var语句在运行过程中动态定义变量。由于栈空间的计算在作用域运行前就计算好的,运行过程中动态定义的变量就无法插入栈中,所以被临时分配到了堆中。既然是在堆中,我们就可以随意的删除它,这样就不会污染到其它作用域了。
(function(){
eval("var i,o");
o=function(){
return {a:"1",b:"2",c:"3"};
}();
for(i in o)eval("var "+i+"=o[i]");
delete i,delete o;
(function(){
alert(a+b+c); //123
alert(i); //Error: i is not defined
})();
})();
其实,对于这个需求有更好的解决办法,这篇只是说堆变量的就不扯过去了。下一篇中咱使用其它办法来解决这个问题。