- 闭包有两部分组成,一个是当前的执行上下文A,一个是在该执行上下文中创建的函数B
- 当B执行的时候引用了当前执行上下文A中的变量就会产出闭包
- 当一个值失去引用的时候就会会标记,被垃圾收集回收机回收并释放空间
- 闭包的本质就是在函数外部保持内部变量的引用,从而阻止垃圾回收
- 调用栈的并不会影响作用域链,函数调用栈是在执行时才确定,而作用域规则是在代码编译阶段就已经确定了
MDN定义:闭包是指这样的作用域foo,它包含了一个函数fn,这个函数fn1可以调用被这个作用域所封闭的变量a、函数等内容
function one() {
var a = 1;
var b = 2;
function two() {
var c = 3;
debugger;
console.log(a,c);
}
return two;
}
let two = one();
two();
闭包的用途
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,不会在 函数调用后被自动清除
闭包的优化
中间没用到的变量闭包会被忽略 ```javascript function one() { var a = 1; function two() {
var b = 2;
function three() {
var c = 3;
debugger;
console.log(a, b, c);
}
three();
} two(); } one();
function one() { var a = 1; function two() { var b = 2; function three() { var c = 3; debugger; console.log(a, c); } three(); } two(); } one();
<a name="hrtS9"></a>
# 闭包的应用场景
- return返回一个函数
```javascript
var n = 10
function fn(){
var n =20
function f() {
n++;
console.log(n)
}
return f
}
var x = fn()
x() // 21
//这里的 return f, f()就是一个闭包,存在上级作用域的引用
- 函数作为参数
```javascript
var a = ‘heisehuoyan’
function foo(){
var a = ‘foo’
function fo(){
} return fo }console.log(a)
function f(p){ var a = ‘f’ p() } f(foo()) /* 输出
- foo / ```
- 自执行函数 ```javascript var n = ‘heisehuoyan’; (function p(){ console.log(n) })() /* 输出
- heisehuoyan / //同样也是产生了闭包p(),存在 window下的引用 n ```
循环赋值
for(var i = 0; i<10; i++){
(function(j){
setTimeout(function(){
console.log(j)
}, 1000)
})(i)
}
/*
因为存在闭包的原因上面能依次输出1\~10,闭包形成了10个互不干扰的私有作用域。将外层的自执行函数去掉后就不存在外部作用域的引用了,输出的结果就是连续的 10。为什么会连续输出10,因为 JS 是单线程的遇到异步的代码不会先执行(会入栈),等到同步的代码执行完 i++ 到 10时,异步代码才开始执行此时的 i=10 输出的都是 10
*/
使用闭包需要注意
闭包容易导致内存泄漏。闭包会携带包含其它的函数作用域,因此会比其他函数占用更多的内存。过度使用闭包会导致内存占用过多,所以要谨慎使用闭包
闭包是怎么回收的
通常,如果引用闭包的函数是一个全局变量,那么闭包会一直存在直到页面关闭;但如果这个闭包以后不再使用的话,就会造成内存泄漏。
- 如果引用闭包的函数是个局部变量,等函数销毁后,在下次 JavaScript 引擎执行垃圾回收时,判断闭包这块内容如果已经不再被使用了,那么 JavaScript 引擎的垃圾回收器就会回收这块内存。
使用闭包的时候,你要尽量注意一个原则:如果该闭包会一直使用,那么它可以作为全局变量而存在;但如果使用频率不高,而且占用内存又比较大的话,那就尽量让它成为一个局部变量。