文章目录
- 一. 闭包
- 1. 介绍闭包
- 2. 闭包的作用
- 3. 闭包与变量
- 二. 闭包引起的内存泄漏
- 1. 闭包是如何引起内存泄漏的
- 2. 如何解决闭包引起的内存泄漏
- 三. 最后
一. 闭包
1. 介绍闭包
有不少开发人员总是搞不清楚匿名函数与闭包两个概念,因此经常混用。同时闭包也是我们前端面试中的高频考点。闭包是指有权访问另一个函数作用域中变量的函数
,而创建闭包的常见形式就是在一个函数的内部创建另一个函数如下所示:
function outer(){
const a =1
function fn(){
console.log(a)
}
}
在浏览器控制台查看发现是存在闭包的
2. 闭包的作用
闭包其实是一种很常见的设计,比如在我们的节流防抖函数,以及我们的Vue框架,React,都大量的使用了闭包。
首先要介绍的是闭包对作用域变量的保护,也就是实现数据的私有
。
列如我们要设计一个函数调用次数的函数,不使用闭包设计如下:
let i = 0;
function fun() {
i++
console.log(`函数调用了${i}次`);
}
但是这种方式被定义的计算变量i容易被修改。
这时如果使用闭包的方式进行设计:
function count() {
let i = 0;
function fun() {
i++
console.log(`函数调用了${i}次`);
}
return fun
}
const fn = count()
fn()
这样做我们外部的代码就无法修改在函数作用域中的变量 i
除了上面介绍到的,闭包还可以实现方法的私有化
,柯里化函数
,匿名自执行函数
等等许多作用,感兴趣的读者朋友可以查阅相关文章。
3. 闭包与变量
作用域链这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值
。下面这个例子可以清晰的说明这个问题:
本意是每个函数都保存1到10的索引,但是其实所有储存在数组中的函数保存的索引都是10,因为每个函数作用域链中都保存着fn函数的活动对象,所以他们引用的都是同一个变量i。
function fn() {
var result = new Array();
for (var i = 1; i < 10; i++) {
result[i] = function () {
return i;
}
}
return result;
}
array = fn();
console.log(array[1]()); // 10
console.log(array[2]()); // 10
那么怎么解决这个问题呢?我们可以通过创建另一个匿名函数强制让闭包的行为符合预期,如下所示:
function fn() {
var result = new Array();
for (var i = 1; i < 10; i++) {
result[i] = function (num) {
return function () {
return num;
};
}(i);
}
return result;
}
array = fn();
console.log(array[1]()); // 1
console.log(array[2]()); // 12
二. 闭包引起的内存泄漏
1. 闭包是如何引起内存泄漏的
提到闭包内存泄漏这个点,这是一直存在歧义的一个点,难道闭包就一定会造成内存泄漏吗?那么怎么解决这个内存泄漏呢?
其实不是使用了闭包就会造成内存泄漏的问题,具体的例子可以看上面的案例,那么关于闭包造成内存泄漏是怎么一回事呢?下面我们可以借助于垃圾回收机制的标记清除法可以看出:
function fn() {
let count = 1;
function fun() {
count++
console.log(`函数调用了${count}次`);
}
return fun
}
const result = fn()
result() // 2
result() // 3
result是一个全局变量,代码执行完毕不会立即销毁,而result使用fn函数,fn用到fun函数,fun函数里面用到count,cout被引用就不会被回收,所以一直存在。此时闭包就引起了内存泄漏
。
但是,并不是所有的内存泄漏都需要进行手动回收的,比如React!里面很多闭包不能回收的
2. 如何解决闭包引起的内存泄漏
如果有些变量的应用的内存过大,同时还造成了内存泄漏,那么这时我们就可以考虑解决这个内存泄漏的问题,那么该如何解决呢?
// 这段代码会导致内存泄露
window.onload = function(){
var el = document.getElementById("id");
el.onclick = function(){
alert(el.id);
}
}
// 解决方法为
window.onload = function(){
var el = document.getElementById("id");
var id = el.id; //解除循环引用
el.onclick = function(){
alert(id);
}
el = null; // 将闭包引用的外部函数中活动对象清除即可
}
三. 最后
关于闭包的的一些琐碎的知识就介绍到这里吧,有疑问的读者朋友可以在评论区提出,关于闭包的更多知识,各位小伙伴可以自行查阅相关文章或者参考《Javascript高级程序设计》中的闭包篇,感觉讲的还是很详细的,感觉又水了一篇文章 (눈_눈)。