原来这才是闭包

634 阅读4分钟

1 前言

如果能够阅读我的上一篇从 JS 执行机制理解变量提升等特性,相信能够有更多的思考~

闭包的用处还是很多的,俺就是在学习 React 的时候了解到 hook也用到了 JavaScript 的闭包机制,所以前来总结学习。

对于闭包,了解一直不深刻,就记得是:在函数 A 里面返回一个函数 B,这样然后在别处调用,这样就能通过函数 B,访问到函数 A里面的变量,即使函数 A 已经执行完毕销毁了,函数 B 仍然能够访问那些变量。

var local = '小林别闹';
function foo() {
    var local = 1;
    function bar() {
        local++;
        return local;
    }
    return bar;
}
var f1 = foo();
console.log(f1());//2
console.log(f1());//3
console.log(f1());//4
var f2 = foo();//新定义的函数,和上面那个是隔开的的,所以下面输出的是2,而不是4
console.log(f2());//2
console.log('local', local);//小林别闹 没有闭包作为接口,只能访问到全局变量local

执行结果如图: image-20210510192254550.png

1.1 目的

这篇文章主要解决两个问题:

  • 加深对于闭包的理解
  • 理解闭包与垃圾回收机制的关系

正常来说,当 foo 函数执行完毕之后,其作用域是会被销毁的,然后垃圾回收器会释放那段内存空间。而闭包却很神奇的将 foo 的作用域存活了下来, fn1 依然持有该作用域的引用,这个引用就是闭包。

2 垃圾清除机制

仔细观察文章开头的代码:你难道没有疑惑嘛?俺有。为什么 f1f2 取得的变量是独立,互不干扰的呢,为什么 f1 重复执行,返回的是 2,3,4....,而不是2呢?函数执行完毕不是会销毁的嘛?

首先解决第一个问题: f1f2foo 函数分别执行的结果,函数有函数作用域,就相当于local 变量在两个作用域分别声明,当然互相独立,互不干扰了。

第二个问题:其实这和我们的垃圾回收机制的引用计数相关了,如果一个变量的引用不为0,那么他不会被垃圾回收机制回收,引用,就是被调用。这里 f1 引用了 foo 函数,f1 执行的时候自然就用的是同一个 local .

3 看一个经典的闭包

for (var i = 1; i <= 10; i++) {
    console.log(i)//打印1-10
	setTimeout(function () {
		console.log(i);//打印10个11
	}, 1000);
}

for (var i = 1; i <= 10; i++) {
	(function () {
		var j = i;
		setTimeout(function () {
			console.log(j);
		}, 1000);
	})();
}//打印1-10
for (let i = 1; i <= 10; i++) {
	setTimeout(function () {
		console.log(i);
	}, 1000);
}//打印1-10

3.1 循环1

第一个for 循环里面,上边能按照我们的想法直接打印出1-10,但是在定时器里面,就是打印的10个11

原因:setTimeout是异步的,根据浏览器的事件循环机制,会等到同步操作全部执行完毕之后,才会执行。但是这个时候 i 已经自增到11了,至于为啥是11不是10,可以去了解一下前置++和后置++的区别

3.2 循环2

第二个 for 循环,我们借助了立即执行函数,立即执行函数是同步的,同时函数也拥有作用域,声明一个变量 j 来保存每次迭代的 i,是不是有点 this_this那味。一个小细节, j 需要用 let 或者 var 声明,不然就是全局变量。

3.3 循环3

第三个 for 循环,我们借助 let 和块级作用域。用 var 就相当于在for循环外面定义了一个var i=1,10次输出共用一个 i,用 let 的话,i 只在自己的作用域内有效,一次++循环过去了,就换了个作用域,自己就不起作用了,当然i++ 就会有一个新的i对应这个作用域。i let 声明的,当前的 i 只对本轮循环有效,每一次循环的 i 都是一个新的变量,JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量 i 时,就是在上一轮的基础上进行的

4 闭包的优劣

优势:就是闭包的特性,能够在全局访问到局部的作用域,这样可以设置私有的变量和方法

劣势:闭包会使得函数中的变量都被保存在内存中,内存消耗很大。

因为知识储备原因,不能像很多大佬内样写的透彻,欢迎大佬指点!