zoukankan      html  css  js  c++  java
  • js闭包

      看完闭包的概念后发现其实闭包和面向对象编程的对象的私有变量的访问很像,就像是你在java中定义了一个对象,然后用private声明了一个私有变量,那么这个私有变量只有该对象内的函数可以访问,在对象外的其他函数是不可以访问的,那么外面的函数要访问应该怎么办呢?面向对象编程中会写对应的get方法来获取私有变量,要访问该私有变量时就调用该对象的get方法,闭包和这个很类似,javascript中局部变量是不可以被全局函数调用的,所以要调用要用一个函数来获取对应的局部变量。所以,可以把父函数当作对象(object)使用,把闭包当作它的公用方法,把内部变量当作它的私有属性。简单来说就是闭包是指有权访问另一函数作用域中的变量的函数。清晰的讲:闭包就是一个函数,这个函数能够访问其他函数的作用域中的变量。

    闭包是依据词法作用域产生的必然结果。通过变相引用函数的活动对象导致其不能被回收,然而形成了依然可以用引用访问其作用域链的结果。

    下面我们来看一个例子:

    function f1(){

      n = 999;

      function f2(){

        console.log(n);  //999

      }

    }

    在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量对f1就是不可见的。这就是javascript语言特有的‘链式作用域’结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

    既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们就可以在f1外部读取它的内部变量了。

    function f1(){

      n = 999;

      function f2(){

        console.log(n);

      }

      return f2;

    }

    var result = f1();  //返回的是f2函数

    result();  //999

    再来一个例子:

    for (var i = 1; i <= 5; i++) {
      setTimeout( function timer() {
          console.log(i);  
      }, 1000 );
      }

    这段代码不仔细看以为会依次输出1 2 3 4 5,但是,一实践发现它其实输出了5 5 5 5 5。这是因为js是单线程的,所以在执行for循环的时候定时器setTimeout被安排到任务队列中排队等待执行,而在等待过程中for循环就已经在执行,等到setTimeout可以执行的时候,for循环已经结束,i的值也已经变成5,所以打印出来五个5,那么我们为了实现预期结果应该怎么改这段代码呢?

    for (var i = 1; i <= 5; i++) {
        (function(i){
            setTimeout( function () {
                  console.log(i);
              },  1000 );
        })(i);
     }

    引入闭包来保存变量i,将setTimeout放入立即执行函数中,将for循环中的循环值i作为参数传递,1000毫秒后同时打印出1 2 3 4 5。

    同时我们返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

    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];

    在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都添加到一个Array中返回了。

    你可能认为调用f1()f2()f3()结果应该是149,但实际结果是调用f1()f2()f3()结果都是16。

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

    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

     https://www.cnblogs.com/onepixel/p/5062456.html

    https://blog.csdn.net/dovlie/article/details/76339244

  • 相关阅读:
    1.2 文本域(含可编辑表格实现)
    JS手册目录
    1.1 文本框
    JS传中文到后台需要的处理
    java基础和面向对象面试题_01
    try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后?
    java面试题:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
    java基础学习_IO流04_用户登录注册案例(IO版)、数据操作流(操作基本数据类型的流)、内存操作流、打印流、标准输入输出流、随机访问流、合并流、序列化流(对象操作流)、Properties属性集合类、NIO(新IO)_day22总结
    思想:java中,父类的方法中传入的形参的数据类型是泛型,子类的方法的形参想只要一种确定的数据类型,子类该如何做呢?
    几种后端开发技术的选型调研
  • 原文地址:https://www.cnblogs.com/tutuj/p/11042195.html
Copyright © 2011-2022 走看看