一个不常见的闭包陷阱:
var test1 = { name: 'Cson' };
var name = 'noName';
with (test1) {
function setName() {
name = 'xiaoc';
}
setName();
}
alert(test1.name);
alert(name);
我们常常在资料中看到类似这样的描述:with打开对象闭包,在对象闭包里设置属性和方法等于为该对象添加属性以及方法。按这种思路理解,上面的结果应该分别是:'xiaoc'以及'noName'.
但是实验结果是:'Cson'以及'xiaoc'。(IE chrome)'xiaoc'以及'noName' (firefox)。
分析:
其实“在with对象闭包里设置属性和方法等于为该对象添加属性以及方法”这种说法并不完美,例外的情况就是当对象闭包里嵌套函数闭包的时候,这时除了firefox还是认同上面那句话之外,ie和chrome会用另一种方法处理。
我们知道javascript的有两个运行周期:解析周期以及执行周期。当处理上面过程的时候,firefox和IE chrome采用了不同的处理方式:
firefox认为:对象闭包内的声明,作用与对象闭包本身(也就是当作test1的属性处理),因此在执行周期的时候才把setName作为test1的一个方法属性创建,所以里面处理的name也就是test1的name属性了。
但是,ie和chrome认为:对象闭包内的声明,不应该作用与对象闭包。在js的解析周期,由于with还没起作用,因此它们把setName声明解析在全局闭包域里,这时就使setName和with成为了"同级",因此setName里设置的name就成为了全局的name变量。
解决方法:
综上所述,我们只需要把对象闭包内的函数声明改成赋值,就可以避免ie和chrome在js解析周期把setName解析在全局闭包里。
function setName() {
name = 'xiaoc';
}
修改为
var setName=function() {
name = 'xiaoc';
}
这样一来,“声明”变成“赋值”,所有浏览器引擎都会在js的执行周期处理该函数。
修改后firefox ie chrome输出均为:'xiaoc'以及'noName'
一个不常见的关于闭包的技巧:
大家应该对这个闭包的经典问题不陌生了:
<div id="a0">a0</div>
<div id="a1">a1</div>
<div id="a2">a2</div>
<div>
function test2() {
for (var i = 0; i < 3; i++) {
document.getElementById('a'+i).onclick= function() { alert(i); }
}
}
test2();
结果会是a0 a1 a2三个元素点击后alert的值均为3,因为闭包使它们使用了同一个i的值。
经典的解决方法是:
function test2() {
for (var i = 0; i < 3; i++) {
(function() {
var j = i;
document.getElementById('a' + i).onclick = function() { alert(j); }
})();
}
}
test2();
但是这样带来了个问题,只有外部的dom元素不解除引用,闭包使整个作用域链上的变量对象被保存在内存,代来了性能上的损耗,有没有更好的方法,不使用闭包解决该问题呢?
一个更好的方法如下:
function test2() {
for (var i = 0; i < 3; i++) {
(document.getElementById('a' + i).onclick = function() { alert(arguments.callee.j); }).j = i;
}
}
test2();
很巧妙地通过为function设置自定义属性的方法,不用闭包,就完美解决了问题。
参考书籍:《javascript高级程序设计》《javascript语言精髓与编程实践》
欢迎转载,请标明出处:http://www.cnblogs.com/Cson/archive/2011/03/31/2001631.html