基本概念

如何产生闭包
当一个嵌套的内部函数引用了嵌套的外部函数的变量(函数)时候,就产生了闭包
闭包到底是什么?
理解:闭包是一个能够访问外部函数作用域内变量的函数

  • 首先,闭包是一个函数
  • 其次,这个函数不仅能方位自己的作用域,还能访问其他函数的作用域

产生闭包条件

  • 函数嵌套
  • 内部函数引用了外部函数的数据(变量或者函数 )
  • 执行外部函数

生命周期

  • 产生:在嵌套内部函数定义执行时就产生了,不是调用
  • 死亡:在嵌套的内部函数成为垃圾对象时
  1. function f1() {
  2. var a = 2;
  3. var b = '111';
  4. function f2() { // 定义函数时就会产生闭包(不用调用该内部函数)
  5. console.log(a);
  6. }
  7. var y1 = function() {
  8. // 这种方式:函数还没有定义,y1 先是 undefined
  9. console.log(a);
  10. };
  11. }
  12. f1();

常见的闭包

  1. 将函数作为另一个函数的返回值
    1. function f1() {
    2. var a = 2;
    3. function f2() {
    4. a++;
    5. console.log(a);
    6. }
    7. return f2; // 函数也能传递
    8. }
    9. var f = f1(); // 变量 f 指向返回的f2函数地址,所以 f 就可以调用 f2,做到a++
    10. f(); // 3
    11. f(); // 4
    12. f = null; // 闭包死亡(包含闭包的函数对象成为垃圾对象)
    理解:
  • f1 函数 return 返回了一个 f2 函数,在调用 var f = f1() 的时候,
  • 就等于 f = return f2,也就是 f 等于 f1() 函数返回的 f2
  • 在执行 f() 时候,就是直接执行 f2 了,此时的变量 a == 2,a++,a=3

引出:闭包的作用

  • 使得函数内部的变量在函数执行完后,任然存活在内存中(延长局部变量生命周期)
  • 让函数外部可以操作(读写)到函数内部的数据(变量或者函数)
  1. 将函数作为实参传递给另一个函数调用 ```javascript function showDelay(msg, timeout) { setTimeout(function() { alert(msg); // 变量 msg 作为内部函数引用外部函数的变量 }, timeout); } showDelay(‘yahaha’, 2000);
  1. <a name="ghdEU"></a>
  2. ### 闭包应用
  3. JavaScript模块化
  4. 1. 函数声明式
  5. ![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)
  6. ```javascript
  7. var module = myModule();
  8. module.doSomething();
  1. 立即执行函数方式(函数自调用)

image.png

  1. myModule2.doSomething();
  2. // 可以直接用,因为myModule2在window中创建了

闭包缺点以及解决

缺点

  • 函数执行完后,函数内的局部变量没有释放,占用内存时间长
  • 容易造成内存泄漏

解决

  • 尽量不用闭包
  • 即使释放内存

内存泄漏和内存溢出

内存溢出

  • 程序运行错误,当程序运行需要的内存超出了剩余内存,抛出内存溢出

内存泄漏

  • 占用内存没有及时释放
  • 内存泄漏积累多了就容易导致内存溢出
  • 常见内存泄漏:
    • 意外全局变量
    • 没有及时销毁计时器或回调函数
    • 闭包

面试题

  1. var name = 'window';
  2. var obj ={
  3. name: 'obj',
  4. getName: function(){
  5. return function(){
  6. return this.name;
  7. }
  8. }
  9. }
  10. consolo.log(obj.getName()()); // window

理解:

  • 这里不是一个闭包,没有引用外部函数的变量,调用 obj.getName() 其实就是调用的 function return this.name,obj.getName() 其实就是一个函数
  • obj.getName()() 这样就是在调用这个函数,此时的就等于在全局环境下执行函数,this 指向 window
  1. var name2 = 'window';
  2. var obj2 ={
  3. name: 'obj2',
  4. getName: function(){
  5. var that = this;
  6. return function(){
  7. return that.name;
  8. }
  9. }
  10. }
  11. consolo.log(obj2.getName()()); // obj2

理解:

  • 这里也是一个闭包,引用了外部函数变量that,调用 obj.getName() 其实就是调用的 function return that.name,obj.getName() 其实就是一个函数
  • 但是需要注意的是的是,返回的是 that 不是 this
  • obj.getName()() 这样就是在调用这个函数,此时的就等于在全局环境下执行函数,但是这个函数返回的是 that.name,这个that存储了 obj2 环境的 this,因此这里返回的是 obj2