一、闭包的定义
百度百科对闭包的定义
闭包就是能够读取其他函数内部变量的函数。
在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
二、对闭包的理解
首先,我们阅读第一句话,也就是百度百科对闭包的定义:闭包就是能够读取其他函数内部变量的函数。那么,我们可以理解为闭包是一段特殊的代码片段,它具有以下几个特点:
1、存在两个函数(函数f1、函数f2)
2、函数f1内部存在一个内部变量n(注意n是f1的内部变量,即局部变量)
3、函数f2可以访问函数内部变量n
正常打码逻辑如下,但是我们发现函数f2并不能访问函数f1的内部变量n
function f1(){
var n=999;
}
function f2(){
alert(n); // error
}
那么,函数2如何访问函数1内部的变量n呢,继续往下读:在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数。
function f1(){
var n=999;
function f2(){
alert(n); //999
}
}
上面代码片段已经具备了闭包的3个特点,我们就可以说它就是一个闭包 。
那么,我们如何理解最后一句话:在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
这句话,是从本质上对闭包进行了个说明,即闭包的作用,闭包存在的意义,如下代码段
function f1(){
n=1;
function f2(){
n++
alert(n);
}
return f2;
}
var result=f1();
result(); // 2
result(); // 3
也有人说,这才是一个完整的闭包,因为它实现了在函数外部访问函数内部的变量。
这都是对闭包本质的一种解读,但是并不是十分的准确,我更倾向于他是闭包的使用方式,或者说是一个有实际使用意义的闭包。
还有一种说法也比较多:凡是访问函数内部的变量的都叫闭包,我对下面两种说法的解读是:
说法一:在函数外部访问函数内部的变量都是闭包
在函数外部访问函数内部的变量都是闭包,但是闭包并不一定都是从函数外部访问函数内部变量。从定义闭包就是能够读取其他函数内部变量的函数上来看,并没有规定一定要从函数外部访问函数内部的变量。能够读取其他函数(f1)内部变量(n)的函数(f2),即下面代码就是闭包。
function f1(){
var n=999;
function f2(){
alert(n); //999
}
}
说法二:凡是访问函数内部的变量的都叫闭包
闭包全部可以访问函数内部的变量,但是可以访问函数内部的变量的不一定是闭包。
如下,函数内部访问到了函数内部的变量,符合说法二,但很明显不符合闭包定义,不是闭包。
function f1(){
var n=1;
alert(n); // 1
}
总结:
根据定义闭包就是能够读取其他函数内部变量的函数,符合下面特点的代码段就是闭包
1、存在两个函数(函数f1、函数f2)
2、函数f1内部存在一个内部变量n(注意n是f1的内部变量,即局部变量)
3、函数f2可以访问函数内部变量n
//最简闭包
function f1(){
var n=999;
function f2(){
alert(n); //999
}
}
三、闭包的进一步解读
从上述文章中我们可以知道这是一个闭包,但是实际上它并没有任何意义
//最简闭包
function f1(){
var n=999;
function f2(){
alert(n); //999
}
}
为什么这么说呢,因为函数f2虽然可以访问到函数f1的内部变量n,但是实际代码执行的话定没有执行,最终会被js的垃圾回收机制判断为无用变量,从而进行回收。那么我们如何让上面代码变得可用呢?
function f1(){
var n=1;
function f2(){
n++
alert(n);
}
return f2;
}
var result =f1()
result(); // 2
result(); // 3
这便是一个有实际应用意义的闭包,所有有好多人认为这才是闭包。
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是2,第二次的值是3。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制回收。
四、闭包强化(练习题)
1、代码:
let foo = function(){
let i = 0;
return function(){
console.log(i++);
}
}
let f1 = foo();
let f2 = foo();
f1();// 0
f2();// 0
f1();// 1
解释:
首先,第一个输出,因为前置运算,i要先参与输出,然后再自增,所以输出为0
第二个输出,因为f1和f2是不同的函数,不共享i变量,所以输出也为0
第三个输出,因为是f1,共享i,所以i加了1,输出为1
2、代码:
function fun(n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n);
}
};
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,?
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,?
//问:三行a,b,c的输出分别是什么?
解释:
参考链接:JS闭包 经典面试题_js闭包面试题_xinyuan_java的博客-CSDN博客
//a: undefined,0,0,0
//b: undefined,0,1,2
//c: undefined,0,1,1
五、闭包的使用场景
理解js闭包10大使用场景,大厂面试官好自为之!_闭包的应用场景_编程界小明哥的博客-CSDN博客
后面不想写了,等有时间再更新吧