1. 闭包
闭包:被函数包裹的变量 = >在内部函数中可以访问外部函数声明的变量
2. 闭包的用途
2.1 结果缓存
闭包不会释放外部变量的引用,能将外部变量值缓存在内存中。如果内存中有,则直接返回;如果没有,则调用函数并进行缓存,保存结果
2.2 封装
2.2.1 输出li的索引值
业务场景:ul有若干个li,每次单击li,输出li的索引值
let lis = document.getElementsByTagName('ul')[0].children;
for( let i = 0;i<lis.length;i++){
// 创建一个匿名立即执行函数
(function(index){
// 对外部变量lis的引用,创建闭包
lis[index].onclick = function(){
console.log(index);
}
})(i);
}
2.2.2 定时器
业务场景:定时器setTimeout()函数与for()循环搭配使用
let arr = ['1','2','3','4'];
for( let i = 0;i<arr.length;i++){
// 创建一个匿名立即执行函数
(function(time){
setTimeout(function(){
// 对外部变量arr的引用,创建闭包
console.log(arr[time])
},time*1000);
})(i);
}
2.2.3 作用域链问题
业务场景:闭包与this属性
var name = "outer";
var obj = {
name:"inner";
method:function(){
// 保存obj对象中的this
var _this = this;
return function(){
return _this.name;
}
}
};
// 调用method()方法,然后在匿名函数的返回值中再去调用_this.name
console.log(obj.method()());// inner
2.2.4 多个相同函数名问题
第一眼看到这道题的同学,头皮发麻的请点赞!
// 第一个foo()函数是具名函数,返回一个具体的对象
function foo(a,b){
console.log(b);
return {
// 第二个foo()函数是第一个foo()函数返回对象的一个属性,指向一个匿名函数
foo:function(c){
// 第三个foo()函数是被返回的函数,会沿着原型链向上查找;
//最终指向第一个foo()函数,即第三个foo()函数和第一个foo()函数实际上是指向同一个函数
return foo(c,a);
}
}
}
var x = foo(0);x.foo(1);x.foo(2);x.foo(3);// undefined,0,0,0
var y = foo(0).foo(1).foo(2).foo(3);// undefinde,0,1,2
var z = foo(0).foo(1);z.foo(2);z.foo(3);// undefined,0,1,1
解析:
var x = foo(0);x.foo(1);x.foo(2);x.foo(3);
1. 由于变量x在执行foo(0)时,没有传入b值,所以是console.log(b)=>输出:undefined
2. 由于第三层foo()函数和第一层foo()函数是同一个函数,且foo()函数闭包了外层的a值,所以实际调用是第一层的foo(1,0)=>输出:0
3. x.foo(2)和x.foo(3)的原理同上,输出0
=================================================
var y = foo(0).foo(1).foo(2).foo(3);
4. 由于变量x在执行foo(0)时,没有传入b值,所以是console.log(b)=>输出:undefined
5. 链式调用foo(1),由于闭包存在(即对a的引用,此时a=1),实际调用为foo(1,0)=>输出:0
6.执行foo(2),此时a的值等于2,返回foo(2,1),,又由于foo()函数指向第一个foo()函数=>输出:1
7.执行foo(3),此时a的值等于3,返回foo(3,2),,又由于foo()函数指向第一个foo()函数=>输出:2
==================================================
var z = foo(0).foo(1);z.foo(2);z.foo(3);
8. foo(0).foo(1)的调用过程和前面的一样=>输出:undefined,0
9. 由于闭包了变量a的值为1,所以当调用z.foo(2)时,实际上是返回foo(2,1)=>输出:1
10. 由于闭包了变量a的值为1,所以当调用z.foo(3)时,实际上是返回foo(3,1)=>输出:1