这部分内容其实我也是还是一知半解.我先分享一个有意思的例子.
function F1() {
a = 50;
var b = 500;
return function F2() {
console.log(a, b, c);
};
}
var fn = F1();
var a = 100;
var b = 1000;
var c = 10000;
fn();
请问输出值是什么?
100 500 10000
为什么是100,500,10000?
我们得先从闭包说起.闭包是什么?网上很多解释,其实闭包的定义很简单:闭包就是能够读取其他函数内部变量的函数.
啥意思?看不懂,看不懂就对了.OK,直白点说,我们的F2函数里没有定义abc,但是我们却能打印出来,这就是闭包相对于正常函数就是.
var a = 100;
var b = 200;
var c = 300;
function F1(a, b, c) {
console.log(a, b, c);
}
F1(a, b, c);
如果是这样的函数,大家一定脱口而出,100 200 300,为什么?
因为这个函数把a,b,c的值传进去了.
但是现在有个问题,怎么样才能形成闭包呢?
这里需要引入一个新知识点,作用域.
作用域就是你可以理解为环境.什么意思呢?就以上面的为例.
我们的var a = 100,b=200,c=300这些代码在全局作用域中,而console.log(a,b,c)在函数F1的局部作用域中.如果我们let a = 100,这时候会形成一个块级作用域.这块咱们暂时不用考虑.
如果某个函数要使用自己没有声明的变量,这时候回去声明这个函数的作用域中寻找,如果还是没有就会再往上寻找.
那我们回到初始的例子.
function F1() {
a = 50;
var b = 500;
return function F2() {
console.log(a, b, c);
};
}
var fn = F1();
var a = 100;
var b = 1000;
var c = 10000;
fn();
a的值变化过程是怎么样的?
想要知道a的值是怎么变化的,首先我们要知道这几行代码的运行顺序.全局环境下,先运行了var fn = F1(),然后对a,b,c赋值,然后执行fn();
运行F1()时,把全局中的a赋值成了50(此前a=undefined,因为js会将变量声明提前.并且这里是一个闭包),并在函数作用域里定义了b为500.
对全局作用域下的a,b,c赋值.此时有4个变量.
全局作用域:a = 100,b = 1000,c = 10000
F1作用域:b = 500
然后执行fn(),相当于执行F2(执行F1函数的返回函数).
此时我们的F2函数作用域里是没有a,b,c的.
此时往声明F2的作用域–F1中寻找,这里有b=500,a,c还是没有,这时候往声明F1的作用域–全局中寻找,a = 100,c=10000.那么输出的时候就是100,500,10000了.这时候聪明的小朋友就又有了一个疑惑.
function F1() {
var a = 100;
console.log(a);
}
F1();
console.log(a);
这样输出的值是什么?
100 和 报错.因为F1函数执行完就没他什么事了.全局中没有定义a,那么这里浏览器就会报a未定义.
所以闭包只会由内而外去寻找,而不会由外而内去寻找.
function f1() {
var n = 999;
nAdd = function () {
n += 1;
};
function f2() {
console.log(n);
}
return f2;
}
var result = f1();
result(); //999
nAdd();
result(); // 1000
f1()();//999
nAdd();
f1()();// 999
闭包还有一个作用可以在这个例子里显现出来,就是使你定义再函数内部的变量不会被销毁.
例如上面例子中的f1中的n,正常我们在执行完函数之后会gc回收掉,如果跟我一样是后端转前端的同学应该知道垃圾回收器这个东西.就像我们的桌子上本来是空的,需要打草稿计算1+1的时候会拿一张草稿纸写写画画,当我们打完草稿,算出来1+1=2的时候,这张草稿纸就对我们没用了,接下要算2+2等于几了,这时候这张算1+1=2的草稿纸我们就要把它扔到垃圾桶里.这就是垃圾回收的基本原理了.那如果这张草稿纸我们以后还想看看,或者草稿纸上面记录了类似5月14号是晴天之类的别的东西在里面,我们不想把他扔掉.这时候我们就需要形成一个闭包.那么这时候闭包就是我们在算2+2,3+3的时候,还能取出草稿纸看5月14号是晴天,然后把2+2这张草稿纸上也写上5月14号是晴天.
讲这么多,其实对应到上面的例子就是var result = f1()这行代码形成了一个闭包,nAdd()将f1作用域中的n++,之后再result()的时候,我们又可以取出n的值了.而下面 f1()()在运行完之后就已经被销毁了,没有别的地方引用,也没有别的地方可能用到,那就是一张无用的草稿纸,就被回收了.
那么草稿纸要是每页都留着就会占用我们的桌子,让我们没地方放别的东西.所以闭包也有可能会引起内存写漏.这是闭包的缺点.