基本概念
如何产生闭包
当一个嵌套的内部函数引用了嵌套的外部函数的变量(函数)时候,就产生了闭包
闭包到底是什么?
理解:闭包是一个能够访问外部函数作用域内变量的函数
- 首先,闭包是一个函数
- 其次,这个函数不仅能方位自己的作用域,还能访问其他函数的作用域
产生闭包条件
- 函数嵌套
- 内部函数引用了外部函数的数据(变量或者函数 )
- 执行外部函数
生命周期
- 产生:在嵌套内部函数定义执行时就产生了,不是调用
- 死亡:在嵌套的内部函数成为垃圾对象时
function f1() {
var a = 2;
var b = '111';
function f2() { // 定义函数时就会产生闭包(不用调用该内部函数)
console.log(a);
}
var y1 = function() {
// 这种方式:函数还没有定义,y1 先是 undefined
console.log(a);
};
}
f1();
常见的闭包
- 将函数作为另一个函数的返回值
理解:function f1() {
var a = 2;
function f2() {
a++;
console.log(a);
}
return f2; // 函数也能传递
}
var f = f1(); // 变量 f 指向返回的f2函数地址,所以 f 就可以调用 f2,做到a++
f(); // 3
f(); // 4
f = null; // 闭包死亡(包含闭包的函数对象成为垃圾对象)
- f1 函数 return 返回了一个 f2 函数,在调用 var f = f1() 的时候,
- 就等于 f = return f2,也就是 f 等于 f1() 函数返回的 f2
- 在执行 f() 时候,就是直接执行 f2 了,此时的变量 a == 2,a++,a=3
引出:闭包的作用
- 使得函数内部的变量在函数执行完后,任然存活在内存中(延长局部变量生命周期)
- 让函数外部可以操作(读写)到函数内部的数据(变量或者函数)
- 将函数作为实参传递给另一个函数调用 ```javascript function showDelay(msg, timeout) { setTimeout(function() { alert(msg); // 变量 msg 作为内部函数引用外部函数的变量 }, timeout); } showDelay(‘yahaha’, 2000);
<a name="ghdEU"></a>
### 闭包应用
JavaScript模块化
1. 函数声明式
![image.png](https://cdn.nlark.com/yuque/0/2020/png/236053/1586537830439-b7ccf9c3-a7cd-4834-a9f7-ae9053358233.png#align=left&display=inline&height=159&name=image.png&originHeight=318&originWidth=480&size=131707&status=done&style=none&width=240)
```javascript
var module = myModule();
module.doSomething();
- 立即执行函数方式(函数自调用)
myModule2.doSomething();
// 可以直接用,因为myModule2在window中创建了
闭包缺点以及解决
缺点
- 函数执行完后,函数内的局部变量没有释放,占用内存时间长
- 容易造成内存泄漏
解决
- 尽量不用闭包
- 即使释放内存
内存泄漏和内存溢出
内存溢出
- 程序运行错误,当程序运行需要的内存超出了剩余内存,抛出内存溢出
内存泄漏
- 占用内存没有及时释放
- 内存泄漏积累多了就容易导致内存溢出
- 常见内存泄漏:
- 意外全局变量
- 没有及时销毁计时器或回调函数
- 闭包
面试题
var name = 'window';
var obj ={
name: 'obj',
getName: function(){
return function(){
return this.name;
}
}
}
consolo.log(obj.getName()()); // window
理解:
- 这里不是一个闭包,没有引用外部函数的变量,调用 obj.getName() 其实就是调用的 function return this.name,obj.getName() 其实就是一个函数
- obj.getName()() 这样就是在调用这个函数,此时的就等于在全局环境下执行函数,this 指向 window
var name2 = 'window';
var obj2 ={
name: 'obj2',
getName: function(){
var that = this;
return function(){
return that.name;
}
}
}
consolo.log(obj2.getName()()); // obj2
理解:
- 这里也是一个闭包,引用了外部函数变量that,调用 obj.getName() 其实就是调用的 function return that.name,obj.getName() 其实就是一个函数
- 但是需要注意的是的是,返回的是 that 不是 this
- obj.getName()() 这样就是在调用这个函数,此时的就等于在全局环境下执行函数,但是这个函数返回的是 that.name,这个that存储了 obj2 环境的 this,因此这里返回的是 obj2