JavaScript高级之闭包

Posted 二木成林

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript高级之闭包相关的知识,希望对你有一定的参考价值。

闭包

如何产生闭包:当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包。即产生闭包的条件如下:

  • 函数嵌套
  • 内部函数引用了外部函数的数据(变量或函数)
<script type="text/javascript">
    function fn1() 
        var a = 2;
        var b = 'hello world';

        function fn2() 
            // 在嵌套的内部函数fn2引用了外部函数fn1中的变量a
            console.log(a);
        
    

    // 执行函数定义就会产生闭包(不用调用内部函数)
    fn1();
</script>

注意:闭包存在于嵌套的内部函数中。

常见的闭包

闭包常见的情况:

  • 将函数作为另一个函数的返回值。
  • 将函数作为实参传递给另一个函数调用。
<script type="text/javascript">
    // 1.将函数作为另一个函数的返回值
    function fn1() 
        a = 1;

        function fn2() 
            a++;
            console.log(a);
        

        return fn2;// 将函数fn2作为函数返回值
    

    var f = fn1();
    f();// 2
    f();// 3

    // 2.将函数作为实参传递给另一个函数调用
    function sleep(msg, time) 
        // 将函数作为实参传递给setTimeout()
        setTimeout(function () 
            console.log(msg);
        , time);
    
    sleep('hello world', 3000);
</script>

闭包的作用

闭包的作用如下:

  • 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
  • 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
<!--
问题:
  1. 函数执行完后, 函数内部声明的局部变量是否还存在?  一般是不存在, 存在于闭中的变量才可能存在
  2. 在函数外部能直接访问函数内部的局部变量吗? 不能, 但我们可以通过闭包让外部操作它
-->
<script type="text/javascript">
    function fn1() 
        var a = 2;

        function fn2() 
            // 延长了局部变量的生命周期
            a++;
            console.log(a);
            // 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
            return a;
        

        function fn3() 
            a--;
            console.log(a);
        

        return fn2;
    

    var fn = fn1();
    fn();// 3
    fn();// 4
    console.log(fn());// 5
</script>

闭包的生命周期

  • 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
  • 死亡: 在嵌套的内部函数成为垃圾对象时
<script type="text/javascript">
  function fn1() 
    //此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
    var a = 2
    function fn2 () 
      a++
      console.log(a)
    
    return fn2
  
  var f = fn1()
  f() // 3
  f() // 4
  f = null //闭包死亡(包含闭包的函数对象成为垃圾对象)
</script>

闭包的应用

闭包可以用来自定义js模块:即具有特定功能的js文件,将所有的数据和功能都封装在一个函数内部并且是私有的,对外只暴露一个包含多个方法的对象或者函数,而模块的使用者只需要通过暴露的对象调用方法来实现对应的功能。在js模块中有两种暴露的方式:

  • 普通函数返回对象

hello.js:封装的模块

function hello() 
    // 私有属性
    var msg = "hello world!";

    // 操作数据的函数
    function upper() 
        console.log(msg.toUpperCase());
    

    function lower() 
        console.log(msg.toLowerCase());
    

    // 向外暴露对象,即提供给外部访问的方法
    return 
        upper: upper,
        lower: lower
    

test.html:测试使用hello.js模块

<script type="text/javascript" src="hello.js"></script>
<script type="text/javascript">
    // 获取hello()方法,该方法返回的是一个对象
    var hello = hello();
    // 调用该对象暴露出的方法
    hello.lower();// hello world!
    hello.upper();// HELLO WORLD!
</script>
  • 立即执行函数暴露对象

hello.js:自定义的功能模块

(function () // 立即执行函数
    // 私有属性
    var msg = "hello world!";

    // 操作数据的函数
    function upper() 
        console.log(msg.toUpperCase());
    

    function lower() 
        console.log(msg.toLowerCase());
    

    // 向外暴露对象,即提供给外部访问的方法
    // 给window对象添加一个属性,该属性名自定义,并且属性值为向外暴露的对象
    window.hello = 
        upper: upper,
        lower: lower
    
)();

test.html:测试hello.js模块

<script type="text/javascript" src="hello.js"></script>
<script type="text/javascript">
    // 可以直接通过对象名进行调用,因为该对象是window上的属性
    hello.lower();
    hello.upper();
</script>

闭包的缺点及解决

<!--
1. 缺点
  * 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
  * 容易造成内存泄漏
2. 解决
  * 能不用闭包就不用
  * 及时释放
-->
<script type="text/javascript">
  function fn1() 
  	// 在闭包中,这里占用的内存空间一直不会被释放,所以造成了内存泄漏
    var arr = new Array[100000]
    function fn2() 
      console.log(arr.length)
    
    return fn2
  
  var f = fn1()
  f()

  f = null //让内部函数成为垃圾对象-->回收闭包
</script>

以上是关于JavaScript高级之闭包的主要内容,如果未能解决你的问题,请参考以下文章

深入浅出JavaScript之闭包(Closure)

深入浅出JavaScript之闭包(Closure)

Javascript闭包

javascript 闭包

再谈闭包

Javascript中 闭包问题