闭包速记
什么是闭包?
谈到闭包,很多人总是会对闭包的概念比较模糊,简单来说如下:
- 闭包就是能够读取其他函数内部变量的函数;
- 闭包是指有权能够访问另外一个函数作用域中变量的函数 ,创建闭包的最常见方式就是在一个函数内部创建另一个函数,通过另外一个函数访问这个函数的局部变量,利用闭包可以突破作用域链。
闭包的特性如下:
- 函数内再嵌套函数;
- 内部函数可以引用外层的参数和变量;
- 参数和变量不会被垃圾回收机制回收;
谈一下对闭包的理解?
- 使用闭包主要是为了设计私有化的方法和变量 。闭包的优点可以避免全局变量的污染 ,缺点是闭包会常驻内存,会增大内存的使用量 ,使用不当很容易造成内存泄漏。在
js
中,函数即闭包,只有函数才会产生作用域的概念。 - 闭包的最大用处有两个,一个是可以读取函数内部的变量 ,另外一个就是让这些变量始终保存在内存中。
- 闭包的另外一个好处就是封装对象的私有属性和私有方法。
- 使用闭包的好处就是实现封装和缓存 ,坏处就是消耗内存,不正当使用会造成内存溢出问题。
使用闭包注意项:
- 由于闭包会使得函数中的变量都保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在 IE 中可能会导致内存泄漏。
- 如何解决呢:就是在退出函数之前,将不使用的变量全部清除。
闭包的应用:
闭包在Javascript
中非常常见,它允许函数访问和操作函数外部的变量。最常见的应用之一是创建私有变量和私有方法。
例如:我们可以使用闭包来创建一个计时器:
js
/**
* 闭包实现计时器
*/
function createCounter() {
let count = 0;
return {
increment() {
count++;
},
getCurrentCount() {
return count;
}
}
}
const counter = createCounter();
counter.increment();
console.log(counter.getCurrentCount()); //输出1
上述例子中,createCounter
函数返回了一个对象,该对象有两个方法:increment
和getCurrentCount
。这两个方法都可以访问和操作createCounter
函数内部的私有变量count
。由于createCounter
函数的执行上下文(包括其变量对象)在返回的对象方法被调用时依然存在,因此这些方法形成了一个闭包,可以访问和操作count
。
使用闭包造成内存泄漏如何解决:
当闭包引入了一个外部函数的变量,并且这个闭包被长期保存 (例如被赋值给一个全局变量或者被加入到一个长期存在的数据结构中),那么外部函数的执行上下文(包括变量对象)就不能被垃圾收集器回收 ,即使外部函数本身已经不需要了。这种情况可能会导致内存泄漏。
js
function createLeakyCounter() {
let count = 0;
return {
increment: function () {
count++;
}
}
}
// 将闭包赋值给一个全局变量
window.leakyCounter = createLeakyCounter();
// 假设其他代码在后续的执行过程中不断调用 leakyCounter.increment()
上述例子中,createLeakyCounter
函数返回了一个只有increment
方法的对象。这个对象被赋值给了全局变量leakyCounter
,因此它不会被垃圾收集器回收。而leakyCounter
又引用了createLeakyCounter
函数内部的变量count
,导致createLeakyCounter
的执行上下文也不能被回收。即使createLeakyCounter
函数本身不再需要,它的执行上下文仍然会占据内存,造成内存泄漏。
解决方法:解决由闭包引起的内存泄漏的一种常见的方法是解除不必要的引用。
在上述例子中我们可以将leakyCounter
设为null
来解除对createLeakyCounter
执行上下文的引用。当没有其他代码再引用leakyCounter
时,它的执行上下文就可以被垃圾收集器回收了,从而避免了内存泄漏。
js
window.leakyCounter = null;