本帖最后由 小蜀哥哥 于 2019-9-12 12:21 编辑
闭包——JS三座大山之一,对很多前端开发者来说是个非常头疼的知识点。本文将花费大约9分钟时间,用4组非常简单的例子来对比理解闭包。
在案例对比之前,先给大家一个观点:闭包只是一种现象。我们不要试图用官方的定义,以扣字眼的方式去理解闭包的含义,如果看完下面的4组案例后,能搞清楚闭包这种现象是如何产生的,那就大功告成了。
第一组、外部引用再执行
[JavaScript] 纯文本查看 复制代码 var m, n;//全局变量
function outer_a1() {
var num = 10;
function inner() {
console.log(num) //打印了outer_a1中定义的num
debugger
}
m = inner;
}
outer_a1();
m();
function outer_a2() {
var num = 10;
function inner() {
console.log("just console") //只做了简单的打印
debugger
}
n = inner;
}
outer_a2();
n();
上面两张图片是分别执行outer_a1()和outer_a2()后再执行m()和n()时,到其中的debugger断点后,chrome控制台的截图,我们可以看出第一个出现了Closure并包含了变量num:10,第二张图并没有出现Closure。先不做解释,继续第二组案例对比。
第二组、内部直接执行
[JavaScript] 纯文本查看 复制代码 function outer_b1() {
var num = 15;
function inner() {
console.log(num) //打印了outer_b1中定义的num
debugger
}
inner()
}
outer_b1();
function outer_b2() {
var num = 15;
function inner() {
console.log("just console") //只做了简单的打印
debugger
}
inner()
}
outer_b2();
第二组跟第一组差不多,只是改为了在outer_b1和outer_b2中直接执行了inner,我们可以发现还是第一个产生了Closure包含变量num:15(备注:如果把var num = 15;放在inner()执行的后面,根据变量提升规则,Closure中的变量num值就是undefined),第二个并没有产生Closure,继续第三组:
第三组、return后再执行
[JavaScript] 纯文本查看 复制代码 function outer_c1(s) {
var num = 20;
function inner() {
console.log(s + num) //打印包含了outer_c1形参s和定义的num
debugger
}
return inner
}
outer_c1()();
function outer_c2(s) {
var num = 20;
function inner() {
console.log("just console") //只做了简单的打印
debugger
}
return inner
}
outer_c2()();
第三组使用了return把内部的inner返回出来,然后分别执行outer_c1()()和outer_c2()()来运行内部的inner,发现还是第一个产生了Closure而第二个没有Closure,不同的是第一个又引用了outer_c1的形参s,所以Closure中的变量也多了一个s,不过执行outer_c1()时并没有传入实参,所以s的值是undefined。再来看下最后一组吧:
第四组、多种方式一起
[JavaScript] 纯文本查看 复制代码 var x, y, z; //全局变量
function outer_d1(s) {
var num = 25;
function inner1() {
console.log(num) //打印定义的num
debugger
}
x = inner1;
function inner2() {
console.log("just console") //只做了简单的打印
debugger
}
inner2();
function inner3() {
console.log(num) //打印定义的num
debugger
}
return inner3
}
outer_d1("i am x")();
x();
function outer_d2(s) {
var num = 25;
function inner1() {
console.log(s) //打印outer_d2的形参s
debugger
}
y = inner1;
function inner2() {
console.log("just console") //只做了简单的打印
debugger
}
inner2();
function inner3() {
console.log(num) //打印定义的num
debugger
}
return inner3
}
outer_d2("i am y")();
y();
function outer_d3(s) {
var num = 30;
function inner1() {
console.log("just console") //只做了简单的打印
debugger
}
z = inner1;
function inner2() {
console.log("just console") //只做了简单的打印
debugger
}
inner2();
function inner3() {
console.log("just console") //只做了简单的打印
debugger
}
return inner3
}
outer_d3("i am z")();
z();
最后一组对比了三个例子就不上截图了,就是上面三组一起上,大家可以自己运行下,结果会发现:
- 1、运行到outer_d1和outer_d2内部的各个inner时都会有Closure,运行到outer_d3内部的各个inner时没有Closure。
- 2、运行到outer_d1内部的各个inner时产生的Closure的变量为num,运行到outer_d2内部的各个inner时产生的Closure的变量为num和s,s的值为各自的实参数据。
- 3、outer_d1和outer_d2内部的inner2虽然只是做了简单的打印,但是也产生了Closure。
总结
闭包是函数在特定情况下执行产生的一种现象,这种现象产生的条件为:当一个函数(outer)运行时它的形参或者它的局部变量被其他函数所引用。满足这个条件时,那么这个outer就会形成闭包,这个闭包所包含的变量为outer本身运行时被其他函数引用到的所有形参和局部变量。形参和变量被引用的方式可以是内部直接运行(包括自执行匿名函数)、被赋值给外部变量、或者是被return。
|