zoukankan      html  css  js  c++  java
  • 深入理解JS函数作用域链与闭包问题

    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,?,?,?
    var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
    var c = fun(0).fun(1);  c.fun(2);  c.fun(3);//undefined,?,?,?
    
    //问:三行a,b,c的输出分别是什么?

      这是一道非常典型的JS闭包问题。其中嵌套了三层fun函数,搞清楚每层fun的函数是那个fun函数尤为重要。可以先在纸上或其他地方写下你认为的结果,然后展开看看正确答案是什么?

    //答案:
    //a: undefined,0,0,0
    //b: undefined,0,1,2
    //c: undefined,0,1,1

      要分析此问题,我们需要了解:

    一、三个fun函数的关系是什么?

      这段代码中出现了三个fun函数,所以第一步先搞清楚,这三个fun函数的关系,哪个函数与哪个函数是相同的。

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

      先看第一个fun函数,属于标准具名函数声明,是新创建的函数,他的返回值是一个对象字面量表达式,属于一个新的object。这个新的对象内部包含一个也叫fun的属性,属于匿名函数表达式,即fun这个属性中存放的是一个新创建匿名函数表达式。

      注意:所有声明的匿名函数都是一个新函数。

      所以第一个fun函数与第二个fun函数不相同,均为新创建的函数。

    二、函数作用域链的问题

      再说第三个fun函数之前需要先说下,在函数表达式内部能不能访问存放当前函数的变量。

      测试1,对象内部的函数表达式:

    var o={
      fn:function (){
        console.log(fn);
      }
    };
    o.fn();//ERROR报错:fn is not defined

      测试2,非对象内部的函数表达式:

    var fn=function (){
      console.log(fn);
    };
    fn();//function (){console.log(fn);};正确

      结论是:使用 var 或是非对象内部的函数表达式内,可以访问到存放当前函数的变量;在对象内部的则不能访问到。

      原因也非常简单,因为函数作用域链的问题,采用var的是在外部创建了一个fn变量,函数内部当然可以在内部寻找不到 fn 后向上级作用域查找 fn,而在创建对象内部时,因为没有在函数作用域内创建 fn,所以无法访问

      所以综上所述,可以得知,最内层的return出去的fun函数不是第二层fun函数,是最外层的fun函数

      所以,三个fun函数的关系也理清楚了,第一个等于第三个,他们都不等于第二个。

     三、到底在调用哪个函数?

      再看下原题,现在知道了程序中有两个fun函数(第一个和第三个相同),遂接下来的问题是搞清楚,运行时他执行的是哪个fun函数?

    1、第一行a

    var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);

      可以得知,第一个fun(0)是在调用第一层fun函数。第二个fun(1)是在调用前一个fun的返回值的fun函数,所以:后面几个fun(1),fun(2),fun(3),函数都是在调用第二层fun函数。因此:

      在第一次调用fun(0)时,o为undefined;

      第二次调用fun(1)时m为1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1,n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;

      第三次调用fun(2)时m为2,但依然是调用a.fun,所以还是闭包了第一次调用时的n,所以内部调用第一层的fun(2,0);所以o为0

      第四次同理;

      即:最终答案为undefined,0,0,0

    2、第二行b

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

      先从fun(0)开始看,肯定是调用的第一层fun函数;而他的返回值是一个对象,所以第二个fun(1)调用的是第二层fun函数,后面几个也是调用的第二层fun函数。因此:

      在第一次调用第一层fun(0)时,o为undefined;

      第二次调用 .fun(1)时m为1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1,n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;

      第三次调用 .fun(2)时m为2,此时当前的fun函数不是第一次执行的返回对象,而是第二次执行的返回对象。而在第二次执行第一层fun函数时时(1,0)所以n=1,o=0,返回时闭包了第二次的n,遂在第三次调用第三层fun函数时m=2,n=1,即调用第一层fun函数fun(2,1),所以o为1;然后闭包了第三次的n就为2了

      第四次调用 .fun(3)时m为3,闭包了第三次调用的n,同理,最终调用第一层fun函数为fun(3,2);所以o为2;

      即最终答案:undefined,0,1,2

    3、第三行c

    var c = fun(0).fun(1);  c.fun(2);  c.fun(3);

      根据前面两个例子,可以得知:

      fun(0)为执行第一层fun函数,.fun(1)执行的是fun(0)返回的第二层fun函数,这里语句结束,因此c存放的是fun(1)的返回值,而不是fun(0)的返回值,所以c中闭包的也是fun(1)第二次执行的n的值。c.fun(2)执行的是fun(1)返回的第二层fun函数,c.fun(3)执行的是fun(1)返回的第二层fun函数。因此:

      在第一次调用第一层fun(0)时,o为undefined;

      第二次调用 .fun(1)时m为1,此时fun闭包了外层函数的n(即n=1了),也就是第一次调用的n=0,即m=1,n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;

      第三次调用 .fun(2)时m为2,此时fun闭包的是第二次调用的n=1,即m=2,n=1,并在内部调用第一层fun函数fun(2,1);所以o为1;

      第四次.fun(3)时同理,但依然是调用的第二次的返回值,遂最终调用第一层fun函数fun(3,1),所以o还为1

      即最终答案:undefined,0,1,1

    总结:

      分析该问题主要需要了解函数作用域链的问题,这样就能清楚第一个fun其实是和第三个fun是一样的;

      其次就是需要了解闭包的知识,如果要我说什么是闭包,我认为,广义上的闭包就是指一个变量在他自身作用域外被使用了,就叫发生了闭包。所以当第三个fun执行时,闭包将m的值绑定到了n上

  • 相关阅读:
    Java如何编写自动售票机程序
    install windows service
    redis SERVER INSTALL WINDOWS SERVICE
    上传文件
    This problem will occur when running in 64 bit mode with the 32 bit Oracle client components installed.
    解决Uploadify上传控件加载导致的GET 404 Not Found问题
    OracleServiceORCL服务不见了怎么办
    Access to the temp directory is denied. Identity 'NT AUTHORITYNETWORK SERVICE' under which XmlSerializer is running does not have sufficient permiss
    MSSQL Server 2008 数据库安装失败
    数据库数据导出成XML文件
  • 原文地址:https://www.cnblogs.com/goloving/p/8584987.html
Copyright © 2011-2022 走看看