zoukankan      html  css  js  c++  java
  • 关于闭包的理解记录

    问题:如何从从外部读取函数局部变量?

    解决方法:在函数的内部,再定义一个函数

    function lazy_sum(arr) {
        var sum = function () {
            return arr.reduce(function (x, y) {
                return x + y;
            });
        }
        return sum;
    }
    var f = lazy_sum([1, 2, 3, 4, 5]);
    f(); // 15

           从js的作用域结构我们可以知道函数 lazy_sum可以读取sum中的局部变量和参数,那么只要把sum作为返回值,我们不就可以在lazy_sum外部读取它的内部变量了吗!

           在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。

     注1:返回的函数并没有立刻执行,而是直到调用了f()才执行

    function count() {
        var arr = [];
        for (var i=1; i<=3; i++) {
            arr.push(function () {
                return i * i;
            });
        }
        return arr;
    }
    
    var results = count();
    var f1 = results[0];
    var f2 = results[1];
    var f3 = results[2];
    
    f1(); // 16
    f2(); // 16
    f3(); // 16
        原因就在于返回的函数引用了变量i但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了4,因此最终结果为16(因为每个闭包的作用域链中都保存着count函数的活动对象,所以它们引用的是同一个变量i,当count函数执行完后i的值是4,所以闭包中的i也是4
    返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量

    如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变

    function count() {
        var arr = [];
        for (var i=1; i<=3; i++) {
            arr.push((function (n) {
                return function () {
                    return n * n;
                }
            })(i));
        }
        return arr;
    }
    
    var results = count();
    var f1 = results[0];
    var f2 = results[1];
    var f3 = results[2];
    
    f1(); // 1
    f2(); // 4
    f3(); // 9

          创建一个每次循环都会执行一次的匿名函数:将每次循环时包围函数的i值作为参数,存入匿名函数中。因为函数参数是按值传递的,而非引用,所以每个匿名函数中的i值 都为每此循环时i的值

    总结:

    一.什么是闭包

          简单来说,闭包是指可以访问另一个函数作用域变量的函数,一般是定义在外层函数中的内层函数

    二.闭包的作用

        1.读取函数内部的变量

         2.让这些变量的值始终保持在内存中(局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。)

      function f1(){
        var n=999;
        nAdd=function(){n+=1}
          function f2(){
          alert(n);
          }
        return f2;
      }
      var result=f1();
      result(); // 999
      nAdd();
      result(); // 1000

           在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

    为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

    三.使用闭包的注意点

    1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

    内存泄漏:程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

    2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

    四.应用场景

    把多参数的函数变成单参数的函数

    function make_pow(n) {
        return function (x) {
            return Math.pow(x, n);
        }
    }
    // 创建两个新函数:
    var pow2 = make_pow(2);
    var pow3 = make_pow(3);
    
    console.log(pow2(5)); // 25
    console.log(pow3(7)); // 343

    参考链接:1.http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html

                      2.https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/00143449934543461c9d5dfeeb848f5b72bd012e1113d15000

                      3.https://github.com/mqyqingfeng/Blog/issues/9

                      4.https://www.jb51.net/article/126565.htm

                      5.https://blog.csdn.net/u012028371/article/details/79609428

  • 相关阅读:
    递归判断回文
    从小工到专家阅读笔记1
    建立SQL全文索引提升搜索速度
    数据库SQLServer经验小记
    [转]C#中调用SQL存储过程(带输入输出参数的例子)
    20101124 14:55 全文索引是解决海量数据模糊查询的较好解决办法
    使用SQL Server 2008提供的表分区向导
    千万级SQL Server数据库表分区的实现
    C#调用存储过程简单完整例子
    SQL server 海量数据库的查询优化及分页算法(收藏)
  • 原文地址:https://www.cnblogs.com/zhoujingguoguo/p/10193269.html
Copyright © 2011-2022 走看看