闭包是js的一个难点也是它的一个特色,是我们必须掌握的js高级特性,闭包到底是什么呢?
我们知道在es5中有两种作用域,全局和局部;根据我们对作用域链的理解,在内部作用域可以获得当前包含当前作用域的外层作用域下的变量,反之则不行。也就是说在外层作用域下无法获取内层作用域下的变量,同样在不同的函数作用域中也是不能相互访问彼此变量的,那么我们想在一个函数内部也有限权访问另一个函数内部的变量该怎么办呢?闭包就是用来解决这一需求的,闭包的本质就是在一个函数内部创建另一个函数。
函数嵌套函数就形成了一个闭包:
function func() {
var a = 1, b = 2;
function closure() {
return a + b;
}
return closure;
}
console.log(func()()); // 3
我们首先知道闭包有3个特性:
①函数嵌套函数
②函数内部可以引用函数外部的参数和变量
③参数和变量不会被垃圾回收机制回收(保存在内存中)
在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁
-
闭包的用途
闭包可以用在许多地方。它的最大用处有两个 :
1、一个是前面提到的可以读取函数内部的变量
2、另一个就是让这些变量的值始终保持在内存中
闭包案例:1、定时器与闭包
for(var i = 0;i<5;i++){
setTimeout(function() {
console.log(i)
}, 100);
}
原来由于js是单线程的,所以在执行for循环的时候定时器setTimeout被安排到任务队列中排队等待执行,而在等待过程中for循环就已经在执行,等到setTimeout可以执行的时候,for循环已经结束,i的值也已经编程5,所以打印出来五个5
引入闭包来保存变量i,将setTimeout放入立即执行函数中,将for循环中的循环值i作为参数传递
for(var i = 0;i<5;i++){
(function arr (i){
setTimeout(function() {
console.log(i)
}, 100);})(i)
}
在这里面匿名的内部函数调用了外部的arr中的局部变量i 形成了闭包,每次调用的变量保存在内存中,到最后就输出了0,1,2,3,4
react与闭包
Hooks 严重依赖于 JS 闭包。最简单的:如果我们在回调函数中使用了 state 的值,那么闭包就会产生。闭包在函数创建时产生,他会缓存创建时的 state 的值。
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0)
function handle() {
setCount(count + 1)
// 当 setTimeout 执行时,
// 回调函数的 count 值不是 1,而是 0
setTimeout(() => {
setCount(count + 2)
}, 0)
}
return (
<div>
<div>{count}</div>
<button onClick={handle}>递增</button>
</div>
)
}
最后总结一下闭包的好处与坏处
好处:
①保护函数内的变量安全 ,实现封装,防止变量流入其他环境发生命名冲突
②在内存中维持一个变量,可以做缓存(但使用多了同时也是一项缺点,消耗内存)
③匿名自执行函数可以减少内存消耗
坏处:
①其中一点上面已经有体现了,就是被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏,解决方法是可以在使用完变量后手动为它赋值为null;
②其次由于闭包涉及跨域访问,所以会导致性能损失,我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响