闭包是js中一个难懂又必须征服的概念,他的形成与变量作用域以及变量的生存周期密切相关。
变量作用域和生存周期
作用域,按字面理解,就是指变量的有效范围,超出这个范围就无法访问。
在函数中,里面函数可以访问外面的变量,但是外面无法访问内部变量。举个简单例子:
var a = 1;
var fun1 = function() {
var b = 2;
var fun2 = function() {
var c = 3;
alert(b); //2
alert©; //3
}
fun2();
alert©; //c is not defined
};
fun1();
生存周期:全局变量的生存周期是永久的,除非我们主动销毁,但是普通对于用var声明的局部变量,当退出函数不再使用时,就会被销毁。但是当你使用到闭包时,情况就不一样了
var fun = function() {
var a = 1;
return function() {
a++;
alert(a);
}
};
var f = fun();
f(); //2
f(); //3
f(); //4
f(); //5
解释:当执行var f = fun();时,f返回了一个匿名函数的引用,它可以访问到fun()被调用的生产环境,而局部变量a一直在这个环境中。既然局部变量能
被外界访问,那它就有了不被销毁的理由,于是局部变量生命被延续了。
再例如:
var nodes = document.getElemmentByTagName(‘div’); //此处他的长度为5
for (var i = 0, len = nodes.length; i < len; i++) {
nodes[i].onclick = function() {
alert(i);
}
};
以上代码无论点哪个div显示的都是5(div节点的onclick是异步触发的,当事件触发时,循环已经结束,此时i的值是5)
解决方法:闭包大法好
for (var i = 0, len = nodes.length; i < len; i++) {
(function(i) {
nodes[i].onclick = function() {
alert(i);
}
})(i)
};
通常,闭包可以有封装一些不需要暴露在全局的变量,延续局部变量寿命等等用途。
命令模式
命令模式的意图是把请求封装为对象,从而分离请求的发起者和请求的接收者(执行者)之间的耦合关系。在命令执行之前可预先往命令对象中植入命令的接收者。
var TV = {
open: function() {
alert(‘open’);
},
close: function() {
alert(‘close’);
}
};
var createCommand = function(receiver) {
var execute= function() {
return receiver.open(); //执行命令打开电视
}
var undo= function() {
return receiver.close(); //执行命令关闭电视
}
return {
execute: execute,
undo: undo
}
};
var setComment = function(command) {
document.getElementById('execute').onclick = function() {
command.execute();
}
document.getElementById('undo').onclick = function() {
command.undo();
}
};
setCommand(createCommand(tv));