Javascript的高级函数之闭包

闭包的理解

如何产生闭包?

  • 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。

闭包是什么?

  • 理解一:闭包是嵌套的内部函数,例如:下面例子中内部函数fun2()就是一个闭包
 function fun1(){
     var a = '我是父元素定义的变量a';
     var b = '变量b'
     function fun2 (){//fun2()是内部函数,一个闭包
         console.log(a);//使用了父函数中声明的变量a
     }
     fun2();
 }
 fun1()//调用函数fun1()

在这里插入图片描述

  • 理解二:通过debug调试可以查看到,闭包是包含被内部函数引用外部函数的变量(函数)的对象。

  • 通过调式可以看到一个Closure (闭包)里面只显示变量a,没有变量b,故此也可以理解为闭包是被内部函数引用外部函数的变量(函数)的对象。

  • 执行内部函数定义就会产生闭包(不用调用函数)


产生闭包的条件

  • 函数嵌套
  • 内部函数引用了外部函数的数据(变量/函数)

常见的闭包

将函数作为另一个函数的返回值

 function fun1() {
     var a = 0;
     function fun2() {
         a++;
         console.log(a);
     }
     return fun2;
 }
 var f = fun1();
 f();//1
 f();//2
 f();//3

在这里插入图片描述
注意:

  • 该过程只产生了一个闭包
  • 每次调用f()相当于执行了一次fun2()

将函数作为一个实参传递给另一个函数调用

     function show(msg, time) {
         setTimeout(function () {
             console.log(msg);
         }, time)
     }
     show("新奇时间", 1000);
 </script>

在这里插入图片描述


闭包的作用

  • 使用函数内部的变量在函数执行完后,仍然存活在内存中(延长局部变量的生命周期)
  • 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

例如上面的例子中

 function fun1() {
     var a = 0;
     function fun2() {
         a++;
         console.log(a);
     }
     return fun2;
 }
 var f = fun1();
 f();//1
 f();//2
 f();//3
  • 在这个例子中,函数执行完后变量a仍然存活在内存中
  • 可以通过闭包去操作(读写)fun1()中变量a

闭包生命周期

  • 闭包产生:在嵌套内部函数定义执行完成时就产生闭包(不是调用)
  • 闭包死亡:在嵌套的内部函数成为垃圾对象时
 function fun1() {
 //此时闭包就已经产生(函数提升,内部函数对象已经建立)
     var a = 0;
     function fun2() {
         a++;
         console.log(a);
     }
     return fun2;
 }
 var f = fun1();
 f();//1
 f();//2
 f();//3
 f = null;//闭包死亡(包含闭包的函数对象成为垃圾对象)

闭包的应用:定义JS模块

  • 具有特定功能的js文件
  • 将所有的数据和功能都封装在一个函数内部(私有的)
  • 只向外暴露一个包含n个方法的对象和函数
  • 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能

自定义模块

  • 这里先创建一个js文件,命名为myModule.js代码如下
function myModule() {
    //私有数据
    var str = "Hello  World!!!";
    //操作数据的函数
    function showBig() {
        console.log("showBig():" + str.toUpperCase());
    }
    function showSmall() {
        console.log("showSmall():" + str.toLowerCase());
    }
    //向外暴露的对象(给外部使用的方法)
    return {
        showBig: showBig,
        showSmall: showSmall
    }
}
  • 然后将myModule.js文件通过<script>标签在html文件中进行引用
    <script src="./myModule.js"></script>
    <script>
        var fn = myModule();
        fn.showBig();
        fn.showSmall();
    </script>

效果:
在这里插入图片描述
另一种写法:
利用立即执行函数往window里面直接添加方法

(function() {
    //私有数据
    var str = "Hello  World!!!";
    //操作数据的函数
    function showBig() {
        console.log("showBig():" + str.toUpperCase());
    }
    function showSmall() {
        console.log("showSmall():" + str.toLowerCase());
    }
    //向外暴露的对象(给外部使用的方法)
    window.myModule = {
        showBig: showBig,
        showSmall: showSmall
    }
})()
  • 然后将myModule.js文件通过<script>标签在html文件中进行引用
  <script src="./myModule.js"></script>
  <script>
      myModule.showBig();
      myModule.showSmall();
  </script>

在这里插入图片描述


闭包的缺点及解决方法

缺点

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

内存溢出和泄漏

内存溢出

  • 一种程序运行出现的错误
  • 当程序运行时需要的内存超过了剩余的内存时,就会抛出内存溢出的错误

内存泄漏

  • 占用的内存没有及时释放
  • 内存泄漏积累多了就容易导致内存溢出

常见内存泄漏

  • 意外的全局变量,没用使用var、let、const申明,直接使用的变量
function fn(){
   a = new.Array(10000);
}
fn();
  • 没有及时清理的计时器或回调函数
  var time = setInterval(function(){//循环定时器后不清理
      console.log("还有谁?");
  },1000);
  //clearInterval(time);
  • 闭包
 function fun1() {
     var a = 0;
     function fun2() {
         a++;
         console.log(a);
     }
     return fun2;
 }
 var f = fun1();
 f();//1
 f();//2
 f();//3//闭包会产生内存泄漏
 f = null;//回收闭包-->让内部函数成为垃圾对象

解决方法

  • 尽量少使用闭包
  • 及时释放

面试题

面试题一

    //代码片段一
    var name = "The Window";
    var Object = {
        name: "The Object",
        getNameFunc: function () {
            return function () {
                return this.name;
            };
        }
    };
    console.log(Object.getNameFunc()());// ? "The Window"

    //代码片段二
    var name2 = "The Window";
    var Object = {
        name2: "The Object",
        getNameFunc: function () {
            var that = this;
            return function () {
                return that.name2;
            };
        }
    };
    console.log(Object.getNameFunc()());// ?  "The Object"

解析:

  • 代码片段一中没有闭包,相当于函数直接执行,故此this指向window,打印"The Window"
  • 代码片段二中,外部函数先申明一个变量保存了此时的this此时this指向Object,然后内部函数使用了外部函数变量that,产生了闭包,that中值保存的是this指向的Object,故此打印"The Object"
    在这里插入图片描述

面试题二

function fun(n, o){
    console.log(o);
    return{
        fun: function(m){
            return fun(m, n);
        }
    }
}

var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined, ?, ?, ? 
console.log("--------------------");
var b = fun(0).fun(1).fun(2).fun(3);//undefined, ?, ?, ?
console.log("--------------------");
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined, ?, ?, ? 

思路

  • 先找到产生闭包的变量,也就是n,每次打印的是上一次产生的闭包n,没有产生闭包打印就是undefined
  • 如下:
var a = fun(0); //undefined  这里会产生闭包n = 0;故此下面都会打印0
a.fun(1); a.fun(2); a.fun(3);//0, 0, 0

console.log("--------------------");

var b = fun(0).fun(1).fun(2).fun(3);//undefined, 0, 1, 2

console.log("--------------------");
var c = fun(0).fun(1);//undefined, 0   这里会产生闭包n = 1;
	c.fun(2);//1 	这里会产生闭包n = 2,但是会被垃圾回收
	c.fun(3);//1 	这里会产生闭包n = 3 但是会被垃圾回收

在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值