也许你在很多前端面试中都被问及闭包是什么?闭包有什么作用?闭包有什么弊端?但是你真的理解闭包吗?闭包确实对于初学者来说要理解他有些困难,它主要是属于作用域链的问题,下面就由我带领你学习一下或者理解一下JavaScript的闭包。
作用域
我们可以将闭包简单的理解为函数内的函数,首先我们来看一个作用域的示例
1 | function Fn(){ |
2 | var a = 3; |
3 | } |
4 | console.log(a); // 此处是报错了,因为我们在函数外部是无法获取函数内部的变量的 |
那么我们可以将上面的函数修改为如下的函数即可得到a的值
1 | var a = 5; |
2 | function Fn(){ |
3 | var a = 3; |
4 | return a; |
5 | } |
6 | consolog.log(a); //返回5 |
7 | Fn(); //返回 3 |
闭包(函数作为返回值)
假如我们想要进行一个相应的计算后,直接输出相应的结果,那么我们就可以直接使用闭包的方式,代码如下
1 | function Fn(){ |
2 | var a = 3; |
3 | function FnAdd(){ |
4 | return a + 1 |
5 | }; |
6 | return FnAdd; |
7 | } |
8 | Fn(); //返回 4 |
上面的这个例子就是一个实际常用的闭包,我们在函数内部将内部函数return出去,就可以获得函数内部计算的结果。
闭包(函数作为参数传递)
除了上面这种函数作为返回值的方法,闭包还可以将函数作为参数传递,示例如下:
1 | var a = 10; |
2 | Fn = function(num){ |
3 | if(num > a){ |
4 | console.log(x); |
5 | } |
6 | } |
7 | (function(f){ |
8 | var a = 20; |
9 | f(60) |
10 | })(Fn) |
以上示例中,我们可以看到虽然我们再匿名函数中再次指定了a的值,但是我们使用的f(60)传递到Fn中的60与a进行比较,f(60)所比较的a的值应该是10,而并不是匿名函数中的a = 20,我们可以简单的将上面的示例重写成如下,以帮助你更好的理解。
1 | function Fn(){ |
2 | var a = 10 |
3 | return function(num){ |
4 | if(num > a){ |
5 | console.log(x); |
6 | } |
7 | } |
8 | var f = Fn(); |
9 | var a = 20; |
10 | f(60) |
闭包的用途(优点)
1)、可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
2)、逻辑连续,当闭包作为另一个函数调用的参数时,避免你脱离当前逻辑而单独编写额外逻辑。
闭包的缺点
1)、由于闭包会将返回值或者变量值保存在内存中,使用不当会引起内存泄漏的问题,从而导致浏览器奔溃。所以不能滥用闭包。解决方法是,在退出函数之前,将不使用的局部变量全部删除
比如上面第三点提到的示例:
1 | var a = 10; |
2 | Fn = function(num){ |
3 | if(num > a){ |
4 | console.log(x); |
5 | } |
6 | } |
7 | (function(f){ |
8 | var a = 20; |
9 | f(60) |
10 | })(Fn) |
为了防止内存泄漏,我们在获得结果后,我们要讲相应的变量从内存中释放出来,那么久用到了null,我们可以将上面的代码改为如下即可
1 | var a = 10; |
2 | Fn = function(num){ |
3 | if(num > a){ |
4 | console.log(x); |
5 | } |
6 | } |
7 | (function(f){ |
8 | var a = 20; |
9 | f(60) |
10 | })(Fn) |
11 | Fn = null |
2)、闭包会在父函数外部,改变父函数内部变量的值。如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便