闭包
1、闭包的概念
利用函数的嵌套,实现将第一层函数中的局部变量,可以在第一层函数外部修改的过程;也是JavaScript中函数的一种高级应用方式。
函数的执行空间:
函数执行的时候,会开辟一个执行空间,里面的代码就是在这个空间中执行,代码执行完毕后,这个空间就会销毁。
2、形成环境
①有一个函数A,在函数A内部返回一个函数B(函数的嵌套);
function a(){ //函数a
var num = 10;
return function b(){ //返回值,函数b
var int = num;
return int;
}
}
②在函数B中访问函数A的私有作用域变量(内部函数使用外部函数的变量);
function a(){
var num = 10;
return function b(){
var int = num; //函数b访问函数a的变量
return int;
}
}
③在函数A外部,有变量引用函数B(将内部函数返回,在外部函数的外部,接收返回值,执行(相当于执行了内部函数))。
function a(){
var num = 10;
return function b(){
var int = num; //函数b访问函数a的变量
return int;
}
}
var f1 = a(); //调用函数a,执行结果就是函数b
var f2 = f1(); //引用f1(),实际上就是应用函数b
3、闭包的特点
①作用域空间不销毁;
②可以通过闭包语法,从外部访问函数内部的变量;
③保护私有变量。
4、闭包的应用场景(举部分例子)
Ⅰ.循环中的事件
分析:循环中直接创建点击事件,就形成了点击事件在循环中的执行空间,无法保存索引,每次点击必然拿到循环结束的值。解决方式如下:
利用循环,点击ul标签中li的每一项。匿名函数自动执行的特点,开辟一个新的执行空间,将循环中的计数器(i)传入,并接收,再次创建li的点击事件就可以拿到li的每一项。
var li = document.querySelectorAll(".list li");
for(var i=0;i<li.length;i++){
(function(index){ //接收参数
li[index].onclick = function(){
console.log(index);
}
})(i) //传参
}
或者将循环中,声明变量的方式改为let后,直接创建点击事件,同样可以解决问题。这是因为let自带一个块级作用域。
var li = document.querySelectorAll(".list li");
for(let i=0;i<li.length;i++){
li[i].onclick = function(){
console.log(i);
}
}
Ⅱ.给某些系统默认的回调函数传参
如:给计时器的回调函数传参。
function fn(a){ //接收参数
return function(){
console.log(a);
}
}
setTimeout(fn("你好,闭包"),1000); //传入参数
Ⅲ.处理掉全局变量
这样的好处是,如果当前只是一个封装,不只是在当前使用,将来在其他位置也会使用,提供了方便,并解决了全局变量。
var f = (function(){
var a = "hello";
function fn(){
console.log(a + "world");
}
return fn; //这里返回时千万不能带(),否则就是执行
})();
f(); //之后可以直接调用执行