返回
闭包的形成跟变量的作用域以及变量的生存周期密切先关。
当在函数中声明一个变量的时候,如果该变量前面没有带上关键字 var,这个变量就会成为全局变量 ,这当然是一种很容易造成命名冲突的做法。
另外一种情况是用 var 关键字在函数中声明变量,这时候的变量即是局部变量,只有在该函数内部才能访问到这个变量,在函数外面是访问不到的。
例一
var func = function() {
var a = 1;
console.log(a); //输出:1
};
func();
console.log(a); // Uncaught ReferenceError: a is not defined
例二,变量的搜索是从内到外而非从外到 内的。
var a = 1;
var func1 = function() {
var b = 2;
var func2 = function() {
var c = 3;
console.log(b); // 输出:2
console.log(a); // 输出:1
};
func2();
console.log(c); //输出:Uncaught ReferenceError: c is not defined
};
func1();
对于全局变量来说,全局变量的生存周期当然是的永久,除非我们主动销毁这个全局变量。
而对于在函数内用 var 关键字声明的局部变量来说,当退出函数时,这些局部变量即失去了 它们的价值,它们都会随着函数的调用的结束而销毁
例一
var func = function() {
var a = 1;
return function() {
a++;
console.log(a);
};
};
var f = func();
f(); // 输出:2
f(); // 输出:3
f(); // 输出:4
f(); // 输出:5
例二,假设页面上有 5 个 div 节点,我们通过循环来给每个 div 绑定 onclick 事件,按照索引顺序,点击第 1 个 div 时弹出 0,点击第 2 个 div 时出 1,以此类
var nodes = document.getElementsByTagName("div");
for (var i = 0, len = nodes.length; i < len; i++) {
nodes[i].onclick = function() {
alert(i);
};
}
解决方法是在闭包的帮助下,每次循环的 i 值都封闭起来。当在事件函数中顺着作用域链从内到外查找变量 i 时,会先找到被封闭在闭包环境中的 i,如果有 5 个 div,这里的 i 分别 是 0,1,2,3,4
for (var i = 0, len = nodes.length; i < len; i++) {
(function(i) {
nodes[i].onclick = function() {
console.log(i);
};
})(i);
}
例三
var Type = {};
for (var i = 0, type; (type = ["String", "Array", "Number"][i++]); ) {
(function(type) {
Type["is" + type] = function(obj) {
return Object.prototype.toString.call(obj) === "[object " + type + "]";
};
})(type);
}
console.log(Type.isArray([])); // 输出:true
console.log(Type.isString("str")); // 输出:true
console.log(Type.isNumber(5)); // 输出:true
console.log(Type.isString([])); // 输出:false
例一,计算乘积
var mult = function() {
var a = 1;
for (var i = 0, l = arguments.length; i < l; i++) {
a = a * arguments[i];
}
return a;
};
var cache = {};
var mult = function() {
var args = Array.prototype.join.call(arguments, ",");
if (cache[args]) {
console.log("从cache中读取" + cache[args]);
return cache[args];
}
var a = 1;
for (var i = 0, l = arguments.length; i < l; i++) {
a = a * arguments[i];
}
return (cache[args] = a);
};
console.log(mult(1, 2, 3)); // 输出:6
console.log(mult(1, 2, 3)); // 输出:从cache中读取6 6
console.log(mult(1, 2, 3, 4)); // 输出:24
var mult = (function(){
var cache = {};
return function(){
var args = Array.prototype.join.call( arguments, ',' );
if ( args in cache ){
console.log('从cache中读取'+cache[ args ]);
return cache[ args ];
}
var a=1;
for(var i=0,l=arguments.length;i<l;i++){
a = a * arguments[i];
}
return cache[ args ] = a;
}
})();
console.log ( mult( 1,2,3 ) ); // 输出:6
console.log ( mult( 1,2,3 ) );// 输出:从cache中读取6 6
console.log ( mult( 1,2,3,4 ) );// 输出:24
我们对上面的代码做一下优化,对有可能修改或者通用的部分进行提炼
var mult = (function() {
var cache = {};
var calculate = function() {
var a = 1;
for (var i = 0, l = arguments.length; i < l; i++) {
a = a * arguments[i];
}
return a;
};
return function() {
var args = Array.prototype.join.call(arguments, ",");
if (args in cache) {
console.log("从cache中读取" + cache[args]);
return cache[args];
}
return (cache[args] = calculate.apply(null, arguments));
};
})();
console.log(mult(1, 2, 3)); // 输出:6
console.log(mult(1, 2, 3)); // 输出:从cache中读取6 6
console.log(mult(1, 2, 3, 4)); // 输出:24
img 对象经常用于进行数据上报,如下所示
var report = function(src) {
var img = new Image();
img.src = src;
};
report("http://xxx.com/getUserInfo");
var report = (function() {
var imgs = [];
return function(src) {
var img = new Image();
imgs.push(img);
img.src = src;
};
})();