闭包:有权访问其他函数内部变量的函数,在一个函数内部创建一个函数,就形成了闭包,闭包的参数和变量不会被垃圾回收机制回收。闭包可以避免全局变量的污染。
使用场景:
1.闭包的经典应用:绑定事件
假如页面上有5个div,我们通过for循环来给每个div绑定一个事件,每点击一次输出它的索引值。如果没有使用闭包,在循环内部给每个节点添加事件,发现点击后每次输出的值都是一样的。因为onclick事件时异步触发的,当事件触发的时候for循环早就结束了。此时变量i的值是5,。解决办法就是在闭包的帮助下,把每次循环的i值包裹起来:使用立即执行函数将i值作为参数传递进去后再绑定事件
var list=document.getElementById("list");
for (var i = 0; i < list.length; i++) {
(function(i){
list[i].onclick=function(){
console.log(i);
}
})(i);
}
2,封装变量:闭包可以帮助把一些不需要暴露在全局的变量封装成私有变量。
在大型项目当中,为了防止命名冲突,一般会把相应的代码用闭包的形式包裹起来,避免暴露在全局作用域下
假如有一个函数接受number类型的参数并返回这些参数的乘积,你应该怎么做。
第一步你应该会写出下面的代码:
var mult=function(){
var a=1;
for (var i = 0; i < arguments.length; i++) {
a=a*arguments[i];
}
return a;
};
优化代码:假如缓存机制,如果参数相同直接返回这个缓存后的值
var cache = {};
var mult = function() {
var args = Array.prototype.join(arguments, ',');
if (cache[args]) {
return cache[args];
} else {
var a = 1;
for (var i = 0; i < arguments.length; i++) {
a = a * arguments[i];
}
return cache[args] = a;
}
}
继续优化:注意到cache这个变量只在这个函数内部被使用,与其让其和mult函数一起暴露在全局作用域下,还不如把它封装在函数内部,
var mult = (function() {
var cache = {}; //把原先的cache封装在函数的内部,这样可以减少页面内的全局变量
return function() {
var args = Array.prototype.join(arguments, ',');
if (cache[args]) {
return cache.args
} else {
var a = 1;
for (var i = 0; i < arguments.length; i++) {
a = a * arguments[i];
}
return cache[args] = a;
}
}
})(); //立即执行函数后return一个function
提炼函数是代码重构中的一种常见技巧。如果在一个大函数中有一些代码块能够独立出来,那么我们将它封装在一个小函数当中,这样有助于代码复用,如果同时小函数有个好名字,那么也起到注释的作用。如果小函数不在程序的其他地方使用,那么将它放在闭包当中。
var mult = (function() {
var cache = {}; //把原先的cache封装在函数的内部,这样可以减少页面内的全局变量
var caculate = function() {
var a = 1;
for (var i = 0; i < arguments.length; i++) {
a = a * arguments[i];
}
return a;
}
return function() {
var args = Array.prototype.join(arguments, ',');
if (cache[args]) {
return cache[args];
}
cache[args]=caculate.apply(null,arguments);//null指默认的宿主对象
}
})();
3.延续局部变量的寿命
用img对象来进行数据上报时,把img变量用闭包封闭起来,因为在低版本的浏览器用report函数上报数据时存在bug,会丢失数据。
因为img是局部变量,函数执行完毕后局部变量随即被销毁,以至于来不及发出http请求,导致数据丢失。
var report=(function(){
var imgs=[];
return function(src){
var img=new Image();
imgs.push(img);
img.src=src;
}
})();
report("url");//数据上报