• 学习闭包


    学习Javascript闭包(Closure)

    作者: 阮一峰

    日期: 2009年8月30日

    珠峰培训

    闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。

    下面就是我的学习笔记,对于Javascript初学者应该是很有用的。

    一、变量的作用域

    要理解闭包,首先必须理解Javascript特殊的变量作用域。

    变量的作用域无非就是两种:全局变量和局部变量。

    Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。

      var n=999;

      function f1(){
        alert(n);
      }

      f1(); // 999

    另一方面,在函数外部自然无法读取函数内的局部变量。

      function f1(){
        var n=999;
      }

      alert(n); // error

    这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

      function f1(){
        n=999;
      }

      f1();

      alert(n); // 999

    二、如何从外部读取局部变量?

    出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。

    那就是在函数的内部,再定义一个函数。

      function f1(){

        var n=999;

        function f2(){
          alert(n); // 999
        }

      }

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

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

      function f1(){

        var n=999;

        function f2(){
          alert(n); 
        }

        return f2;

      }

      var result=f1();

      result(); // 999

    三、闭包的概念

    上一节代码中的f2函数,就是闭包。

    各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。

    由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。

    所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

    四、闭包的用途

    闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

    怎么来理解这句话呢?请看下面的代码。

      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)回收。

    这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

    五、使用闭包的注意点

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

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

    六、思考题

    如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。

    代码片段一。

      var name = "The Window";

      var object = {
        name : "My Object",

        getNameFunc : function(){
          return function(){
            return this.name;
          };

        }

      };

      alert(object.getNameFunc()());


    代码片段二。

      var name = "The Window";

      var object = {
        name : "My Object",

        getNameFunc : function(){
          var that = this;
          return function(){
            return that.name;
          };

        }

      };

      alert(object.getNameFunc()());

    讲的很清楚明了,连我都懂了,要是我们大学时的老师也能这么讲课。。。他们只会放幻灯片

    这里有个 PPT 用于说明 JS 闭包,说明得很透彻: http://www.gracecode.com/archives/2385/

    呵呵,可以作为面试题了!

    闭包个人感觉是一种描述函数内部的数据结构,来描述函数的运行上下文.Javascript编程精粹 这本书算是讲的比较好一点.

    类是有行为的数据,闭包是有数据的行为。

    阮兄:
    有点疑问:
    function f1(){

        n=999;

        function f2(){
          alert(n);
        }

        return f2;

      }

      var result=f1();

      result(); // 999
    可以写成如下的不也一样么?

    function f1(){

        n=999;

        return n;

      }
    var result=f1();
    alert(result);

    @tt 实际上后种方法每次调用 f1 时,都会声明 n = 999,而且 n 无法保留状态值(严格按照你的代码,其实 n 为全局变量,我理解你的本意为 var n = 999;)。

    而第一种 f1 实际上返回的是个匿名函数,这样 n 作用域被另外个 f2 函数作用域所使用,因此它会保留。n 不会被重复声明,且内容会被保存

    这是我见过最简单易懂的闭包教程。

    支持下。

    博主的博客写的不错,简单易懂,东西涉及的很多方面我都有兴趣,看来是同道中人,^_^

    一文中的!!!!!!!!!!!!
    学习了!!

    想知道思考题的答案,
    我以为是:My Object

    顶楼主,我读了一些文章。不是特明白。
    有个问题。
    记得有人说。外面的函数是closure,
    好像楼主说里面的函数是closure.

    不知道到底哪个是?谢谢。

    楼主讲讲最后一个思考题,没明白

    请版主讲一讲最后一个例子怎么回事,没有看明白

    函数中的this一般是指向window中的变量。

    引用hou的发言:
    请版主讲一讲最后一个例子怎么回事,没有看明白

    上面本人说得不太正确。
    this的指向是由它所在函数调用的上下文决定的,而不是由它所在函数定义的上下文决定的。

    如果非要指向object,可显式的控制--把代码的最后一句改为 alert(object.getName().call(object));

    阮大哥讲的很透彻 受益匪浅

    大道至简,给予我这个初学者很大的帮助,谢谢!

    浅显易懂,很好。

    如下看法,认为有待商榷:
    #1、有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

    #2、这段代码中另一个值得注意的地方,就是“nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

      function f1(){
    test = 10;
        var n=999;
        nAdd=function(){n+=1}
        function f2(){
          alert(n);
        }
        return f2;
      }
    //如果 #1 说法正确,下句会打印10,实际结果是test未定义。
    //alert(test); // error test 未定义
      
    //如果 #2 正确,语句 nAdd(); 位置在何处应该都能执行,测试结果在下面这个位置,也就是语句 var result=f1(); 前。是不能执行的。
    //nAdd();
    var result=f1();
    result(); // 999
      nAdd();
    result(); // 1000

    To 过客:

    函数内部定义的方法和变量,要等到函数执行过以后,才会真正定义

    但是从过客说的里面可以引出另外的问题,当使用这样的代码时。

      function f1(){
    test = 10;
        var n=999;
        nAdd=function(){n+=1}
        function f2(){
          alert(n);
        }
        return f2;
      }

    如果在函数f1定义之前添加变量定义

    var n = 1;
    然后调用
    f1()();
    则显示为999。说明nAdd中的n确实是作为全局变量存在。于是问题就来了——有什么方法让他可以是父函数中定义的n呢?

    大道至简,很不错!~ 这篇文章我要转了...

    引用George Wing的发言:

    函数中的this一般是指向window中的变量。

    this关键字代表的实例会根据环境不同而变化的. 他总是指向owner 看看这篇你大概就动this这个关键字了

    http://www.quirksmode.org/js/this.html

    最后一个题感觉和闭包没什么关系啊,能详细解释一下吗?因为当一个函数作为函数而不是方法来调用的时候,this指向的是全局对象,这在《Javascript权威指南》上说的很清楚,所以答案肯定是“The Window”,和闭包没什么关系啊

    最后一题重点在this

    如果把f2申明成全局变量,道理一样吗?

    太经典了!
    终于理解了,一箭双雕啊!既理解了this的用法,又理解了闭包

    这个例子很不错,真的是一箭双雕

    前面讲得挺好的,浅显易懂。对最后的两个例子搞不清楚为啥。版主能不能具体分析下。
    var obj=function()
    {
    var MyFunc=function()
    {
    alert("hello world");
    }
    return function()
    {
    return MyFunc;
    }
    }()
    var f3=obj();
    var f4=obj();
    alert(f3 === f4);//为啥是TRUE;搞不懂

    最后两个例子很精炼 ^ ^

    尝试解答代码段一:
    getNameFunc: function() {//假设函数名为A
    return function()/*假设函数名为B*/ { return this.name; };
    }
    在函数里面构建函数的时候,闭包产生。
    在函数B内调用函数A的this.name,由于函数A没有name属性,所以就去找全局变量name,找到了,所以返回“The Window”,要是没有找到,则返回“undefined”。

    代码段二可以尝试将代码更改为:
    var _this = this;
    return function() { return _this.name +"__"+ this.name; };

    只有一点没弄懂,如下代码,nAdd在函数外为什么可以有意义?而test不行?想了好久,不知道那里有解答
      function f1(){
    test=10;
        var n=999;
        nAdd=function(){n+=1}
        function f2(){
          alert(n);
        }
        return f2;
      }

    通俗易懂,, 阅览无数教程, 看了这篇, 终于明白了点.

    写得太好了

    这篇文章是阮兄一贯的风格,我喜欢,不过 "Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。"这句有点奇怪,c不一样可以在函数内部直接读取全局变量么?难道不是么?

    阮大哥能不能具体讲下最后的思考题啊? 感觉关键在this

    变量的作用域无非就是两种:全局变量和局部变量。
    这句话值得商榷, 变量的作用域确实只有两种, 不过他们是全局对象和函数.
    你想说的或许是变量的类型有两种?

    理解最后两个例子:

    1 函数中的this指的是调用这个函数的owner
    2 object.getNameFunc()是返回一个函数,并没有执行函数中的代码
    3 增加一个例子0:

    var name = "The Window";
      var object = {
        name : "My Object",
        getNameFunc : function(){
    return (this.name);
          }
      };
    var name = object.getNameFunc();
      alert(name); 

    4 把例子1变成

      var name = "The Window";
      var object = {
        name : "My Object",
        getNameFunc : function(){
          return function(){
            return this.name; //这个this是有上下文的限制的
          };
        }
      };
    var tmp = Object.getNameFunc(); //此时没有执行this.name
    var name = tmp();//这个时候才执行,这时候的this上下文为全局
    alert(name);
    //alert(object.getNameFunc()())

    5 把例子2变成:

    var name = "The Window";

      var object = {
        name : "My Object",

        getNameFunc : function(){
          var that = this;
          return function(){
            return that.name;
          };
        }
      };
    var tmp = Object.getNameFunc(); //这个时候执行了that = this,这里的this上下文是object,所以that指的是object
    var name = Object.getNameFunc(); //这个时候执行了that.name
    alert(name);
    //alert(object.getNameFunc()()); 

    引用Jason的发言:

    但是从过客说的里面可以引出另外的问题,当使用这样的代码时。

      function f1(){
    test = 10;
        var n=999;
        nAdd=function(){n+=1}
        function f2(){
          alert(n);
        }
        return f2;
      }

    如果在函数f1定义之前添加变量定义var n = 1;然后调用f1()();则显示为999。说明nAdd中的n确实是作为全局变量存在。于是问题就来了——有什么方法让他可以是父函数中定义的n呢?

    var n = 1;
    function f1(){
    	var n = 999;
    	nAdd = function(){
    		n++;
    	}
    	function f2(){
    		alert(n);
    	}
    	return f2;
    }
    var b = f1();
    nAdd();//n = 999+1 = 1000
    b();//弹出 n 的值是 1000 (闭包内的变量n)
    alert(n);//弹出n的值是 1 (全局)
    
    全局的函数 nAdd 和 由f1返回的函数中 所使用到的变量n全部为f1函数内的局部变量n,而不是全局变量n,证据就是上面的代码中最后一句 alert(n) 弹出的值是1, 也就是说 nAdd中的n++并没有改变全局变量中n的值.

    你自己描述的是 f1()() 显示的是999,说明 n 是使用的f1内部的变量n,而非是全局变量n,不知道你为什么会有

    说明nAdd中的n确实是作为全局变量存在。
    这种想法呢? 如果想在nAdd中使用全局变量n(即在函数外面定义的n)的话,使用window.n来访问.

    楼主文章中的:


    二、如何从外部读取局部变量?

    这一整大段中的
    n=999;

    根据整篇文章所表达的内容,应该为:
    var n=999;

    因为如果没有加var,则声明的是全局变量,既然是全局变量,则在所有函数内部都是可见的,也就不会存在闭包这种说法.

    请求楼主修正.

    引用小彘的发言:

    前面讲得挺好的,浅显易懂。对最后的两个例子搞不清楚为啥。版主能不能具体分析下。
    var obj=function()
    {
    var MyFunc=function()
    {
    alert("hello world");
    }
    return function()
    {
    return MyFunc;
    }
    }()
    var f3=obj();
    var f4=obj();
    alert(f3 === f4);//为啥是TRUE;搞不懂

    因为f3和f4都指向同一个地址(即MyFunc).


    var obj = (function() {
    var MyFunc=function() {
    alert("hello world");
    }
    return function() {
    return MyFunc;
    }
    })();
    var f3=obj();
    var f4=obj();
    alert(f3 === f4);//为啥是TRUE;搞不懂

    我测试了一下,为什么第一个例子输出的什么都没有是null,第二个我理解是myobject。谁能解释下

    引用小秦的发言:

    请求楼主修正.

    谢谢指出,已更正。

    起初以为函数内用var声明变量,就等于用了this声明,其实不是
    var w=100;
    function f1(){
    //var w=101;
    //this.w=102;
    function f2(){
    document.write(this.w);
    }
    return f2;
    }
    f1()();
    输出:100

    var w=100;
    function f1(){
    var w=101;
    //this.w=102;
    function f2(){
    document.write(this.w);
    }
    return f2;
    }
    f1()();
    输出:100

    var w=100;
    function f1(){
    w=101;
    //this.w=102;
    function f2(){
    document.write(this.w);
    }
    return f2;
    }
    f1()();
    输出:101

    var w=100;
    function f1(){
    //var w=101;
    this.w=102;
    function f2(){
    document.write(this.w);
    }
    return f2;
    }
    f1()();
    输出:102

    看起来函数中的var和this并不是一个概念,函数内的局部变量与函数的属性不是一回事,不过通过上面的情况能够加深理解this和闭包

    很不错的讲解,楼主写的通俗易懂,很棒的理解,很受用!我的qq:290913917 希望有机会成为共同研究javascript和html5的伙伴,谢谢!

    我感觉第一个思考题是不是这样理解:
    首先this指向的是当前运行该函数的对象,
    1、object.getNameFunc()得到了一个函数,函数为function(){return this.name}
    2、object.getNameFunc()(),此时为window运行该函数,所以this指向的是window,所以this.name为The window

    做习题之前有一点需要很清楚:

    内部函数可以访问定义它们的外部函数的参数和变量(除了this和arguments之外)
    如果需要访问对象的name属性的话,就需要显示的定义一个变量that来引用this,而这个变量此时就指向object对象了。

    第一题改成下面这样就很清楚了。getNameFunc的第一个()是属于方法调用,所以this绑定到了object对象,自然this.name为"My Object",但是闭包函数无法访问这个this,它只能访问到全局的this。

    var name = "The Window";
      var object = {
        name : "My Object",
        getNameFunc : function(){
    alert(this.name);
          return function(){
            return this.name;
          };
        }
      };
      alert(object.getNameFunc()());

    写的真不错。。。看了很多文章讲闭包都是云里雾里的。。看了本文才恍然大悟。。。哦原来闭包如此简单。。。。楼主写的不错。。。

    前面讲的我都明白,但是最后两个例子还是不明白,好多处都不懂!
    1. var object = {。。。} 这是在干什么?是在声明一个变量?还是在声明一个类,然后里面有许多属性?

    2 . object.getNameFunc()(); 怎么会有两个括号?


    3. 如何判断 this指向的是object 对象还是全局对象 ?

    闭包是运行时中的概念,不能讲哪个函数是一个闭包!而是哪个函数在运行时存在一个闭包!有时候,好几个函数都可以组成一个闭包呢:
    function ff()
    {
    var local=1;
    this.add1=function()
    {
    return ++local;
    };
    this.add2=function()
    {
    return ++local;
    }
    }

    var f=new ff();

    alert(f.add1());//2
    alert(f.add2());//3

    最后两个例子中,第一个其实不是闭包,第二个是,但第二个例子其实不用那么复杂,直接把第一个例子中的this去掉就可以了。

    为什么第一个运行以后结果是result?!既不是window也不是object....???

     function f1(){
        n=999;
      }

      alert(n); // error

    这段代码在IE8和FIREFOX5上根本不能运行,求解释...

    @eva 
    function f1(){
        n=999;
      }

      alert(n); // error
    很明显啊,全局变量n是在函数f1中定义的,你不调用函数f1,n怎么定义呢
    function f1(){
        n=999;
      }
    f1();
      alert(n);
    这样不就OK了

    At this time enhancements in could also be noticed in a point of contention.

    思考题中的例子貌似是 javascript高级程序设计里的。今天刚看到。

    你真是个有趣的人,为什么我总能在你这里找到我感兴趣的博文?……

    楼主可以解释一下思考题一吗?我看很多人跟我一样都不是很懂思考题一啊,万分感谢!

    引用Legend1988的发言:

    楼主可以解释一下思考题一吗?我看很多人跟我一样都不是很懂思考题一啊,万分感谢!

    同问,为什么this在嵌套函数中的意义不一样

    this始终表示调用者的应用,第一个的闭包返回出来的一个函数,就是在window的环境下调用了这个函数,所以这个this是指向的window,而第二个把this保存在了that中。不知道说清楚没有

    不错,小伙子讲的很到位,比一些书上生硬的翻译要好一些。Good!

    后面第1个例子怎么输出result, result 是什么意思

    @陈锐达:

    根据您的解释,运行结果是不对的!如在A中定义name,执行结果一样

    实际上,我认为闭包只是给外部函数提供了一个操作局部变量的途径,局部变量本身并没有改变,仍然为局部变量。

    我也觉得最后两个例子,好像跟this的关系更大……

    @tt 发问需谨慎, 文中的写法是一种类的写法, 域里有动作,你这个是函数。

    渐渐地对闭包和this有点了解,尤其加上各位网友的激情讨论,并附上示例,通俗易懂!

    引用Joe的发言:
    最后两个例子中,第一个其实不是闭包,第二个是,但第二个例子其实不用那么复杂,直接把第一个例子中的this去掉就可以了。

    不行哦,一定要引用一次,不然还不是闭包。

    引用apple的发言:

    this始终表示调用者的应用,第一个的闭包返回出来的一个函数,就是在window的环境下调用了这个函数,所以这个this是指向的window,而第二个把this保存在了that中。不知道说清楚没有

    我赞成。。

    个人觉着楼主说的不是闭包

    初学者就这么理解闭包,最好不过了。

    思考题答案:The Window、My Object;其实最关键的就是要明白作用域链和闭包所起到的作用,其实闭包说到底就是一个函数,而且函数调用返回后其资源所占用的栈区并没有释放,,所以变量就还保存在内存中,由于作用域链的关系,它会去寻找离其最近的var声明,var声明所在就是该闭包所在的层,从而在这层里面所得到的结果就是它最后的值,于是乎返回值也就是这个最终的值了

    最后两道思考题~ 其实和函数调用还有关系的~ 在以上例子中使用的是函数调用的方式,this这时候是指全局变量

    引用afity的发言:

    最后一题重点在this

    正解,和闭包有什么关系啊

    引用红雨的发言:

    正解,和闭包有什么关系啊

    对于代码片段一

    object.getnameFunc() 返回的匿名闭包函数被全局变量所引用,其中的this指向

    全局变量,当执行时打印The Window 。

    对于代码片段二

    object.getnameFunc() 在返回闭包函数前,将this赋给that,此时getnameFunc是由

    object调用的,故而this指向object,当内部函数被返回时,由于闭包的特性,仍然

    能访问到外部函数中的值,当执行打印My Object 。

    代码段一、
    当一个函数作为函数而不是方法调用的时候,这个this关键字引用全局对象。容易令人混淆的是,当一个嵌套的函数(作为函数)在一个包含的函数中调用,而这个包含的函数是作为方法调用的,这也是成立的:this关键字在包含的函数中有一个值,但是它却(不太直观地)引用嵌套的函数体的内部的全局对象。
    所以第一个打印出来的是"The Window"
    代码段二、
    由于this关键字不是在包含的函数中引用的,而是通过that=this这个调用的,所以这个this不是在闭包内的,因此这个this就不能调用函数体内的全局对象,而是他的局部对象object.name,所以第二个打印出来的是"My Object"

    说多无用,关键是理清函数什么时候作为对象使用,什么时候作为函数使用。

    object.getnameFunc()() 也可以改为object.getnameFunc().call()
    这就是javascript狡猾的地方。

    讲得很易懂 ··· 然后,最后思考题 感觉 是this关键字的问题,暂时还不懂··

    谢谢,关于闭包本身的概念非常清晰, 不过我当初读你这篇文章的时候最大的困惑是为什么要搞出闭包这个东西来, 我这里写了我的理解

    http://www.hetaoblog.com/myblogs/post/javascript-closure-chain-scope-anonymous-function-module.jhtml

    请问阮兄/各位高人,假如某个JavaScript的函数对象已被调用过一次,如何用代码验证/判断当该函数对象被第二次调用时,它是新创建的还是使用的内存中已有的?

    引用tomwang的发言:
    最后一个题感觉和闭包没什么关系啊,能详细解释一下吗?因为当一个函数作为函数而不是方法来调用的时候,this指向的是全局对象,这在《Javascript权威指南》上说的很清楚,所以答案肯定是“The Window”,和闭包没什么关系啊

    完全赞同这位仁兄的看法!!前面闭包讲的不错,但是最后面的思考题出的很烂啊,跟闭包没关系,让人看完了反而糊涂了!

    有个特别大的疑问:

    ……原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,……

    f2真的被赋给一个全局变量了?

    引用hanvslin的发言:

    完全赞同这位仁兄的看法!!前面闭包讲的不错,但是最后面的思考题出的很烂啊,跟闭包没关系,让人看完了反而糊涂了!

    最初我也以为思考题与文章主题无关,这两天在反复看 JavaScript 权威指南 函数一章,都看晕了...
    产生闭包效果的环境必须是嵌套函数的引用被保存到了一个全局作用域里面,也不难理解 阮老师思考题的设计了

    在退出函数之前,将不使用的局部变量全部删除。

    请教一下这个改如何做?不太明白。。。Thanks!

    保留内存值的解释有点不好,即使var result = f1(); 即使这里不赋值给全局变量result,通过f1()();也不会改变n的值,呵呵~

    后面的思考题 阮哥能解释一下吗 
    前面的例子是看懂了 但是思考题还是不会做

    引用SpiderMan的发言:

    感觉这里的例子更好一些 https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Working_with_Closures

    这里对于闭包的讲解好一点:普通的函数内嵌,内部函数是先执行;而闭包则是:先把内部函数赋给外部函数,然后在执行。

    跟阮老师学JS!

    感觉应该从js作用域链及其原理来分析闭包,这样才能彻头彻尾明白!

    引用George Wing的发言:

    上面本人说得不太正确。
    this的指向是由它所在函数调用的上下文决定的,而不是由它所在函数定义的上下文决定的。

    这里说的this才是正确的。如果将此处的this能够轻松的理解,我想本文的最后一道题就懂了。你怎样认为呢?

    每个函数在被调用时,其活动对象都会自动取得两个特殊变量:this和arguments。内部函数在搜索这个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量(这一点通过前面的图可以看得更清楚)。意思就是说找到匿名函数中的this和arguments就不会再往下找了(这里的往下指的是外层的包含函数,和最外层的window全局环境),而匿名函数的this对象通常指向window,所以输出的是全局的那个字符串。不过,把外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了

    不执行调用f1肯定不可能生成test变量,第二个问题同样的原因。


    引用过客的发言:

    浅显易懂,很好。

    如下看法,认为有待商榷:
    #1、有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

    #2、这段代码中另一个值得注意的地方,就是“nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

      function f1(){
    test = 10;
        var n=999;
        nAdd=function(){n+=1}
        function f2(){
          alert(n);
        }
        return f2;
      }
    //如果 #1 说法正确,下句会打印10,实际结果是test未定义。
    //alert(test);// error test 未定义
      
    //如果 #2 正确,语句 nAdd(); 位置在何处应该都能执行,测试结果在下面这个位置,也就是语句 var result=f1(); 前。是不能执行的。
    //nAdd();
    var result=f1();
    result(); // 999
      nAdd();
    result(); // 1000

    引用George Wing的发言:

    上面本人说得不太正确。
    this的指向是由它所在函数调用的上下文决定的,而不是由它所在函数定义的上下文决定的。

    哦。看了你这个解释就了解了

    说的很易懂,最后两个例子还是有点模糊

    为什么话要反着说呢,应该说正例,这着用太糟糕了,赞同 @迷途小书童 @steven

    引用tomwang的发言:

    最后一个题感觉和闭包没什么关系啊,能详细解释一下吗?因为当一个函数作为函数而不是方法来调用的时候,this指向的是全局对象,这在《Javascript权威指南》上说的很清楚,所以答案肯定是“The Window”,和闭包没什么关系啊

    《Javascript权威指南》上说:如果嵌套函数作为函数调用,其this值不是全局对象(非严格模式下)就是undefined(严格模式下); 如果嵌套函数作为方法调用,其this值指向调用它的对象。所以代码片段一,getNameFunc()作为object的方法,所以this值应该指向调用它的对象(object),而object自己定义name为"My Object",所以片段一,我觉得应该是弹出"My Object"。我是新手,我只是从this方向对这个函数分析,有错误的地方,还望高手指点。

    “闭包的概念”这一节总结的非常好,浅显易懂。同时,也了解了闭包的两个用处:一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

    最后两个例子关键是区分object.getnameFunc()调用时和调用后的this指向。
    第一个问题,object.getnameFunc()()函数在执行时this属于全局域,因此结果是the window
    第二个问题,通过使用that=this 保留了在调用object.getnameFunc()时的this状态值。因此在getnameFunc()的闭包中访问的变量是已经不是this的了,而是that的~

    JS新手,学习了,思考题和讨论很强大。

    思考题解答(个人见解):
    1、因为闭包最后的返回值是一个函数,注意紧紧是一个函数而已 并没有执行,等到alert调用时才执行,而这时执行调用的方法,前面阮老师说了,函数内没有var声明的事全局变量,所以调用的getName是一个全局的变量,所以对应this找到的是全局的name.
    2、因为this的对象引用在编译时就已经确定了,就是object,所以返回值在Object中开始查找,找到了Obeject中的name就不到全局变量中查找。

    综合各种理解之后,我明白最后的“代码片段一”中的闭包函数中的this是window对象,也就是说..闭包函数的范围竟然是window。。。

    这么理解还不如不理解,这样下来对新手是一种误导。所以说才建议大家尽量都去看英文网站。唉

    其实看完这篇文章,对于闭包的概念就有了更深刻的理解
    最后留的两道题目,很好的说明了这一点。
    因为第一题中getNameFunc这个方法或者叫函数是属于全局作用域的,所以里面返回的this始终都是指向window的。而第二题中用that=this改变了当前函数指向的作用域,所以第二题中的this最终只想的是myobject。
    不知道我的理解正确不正确。
    如果说错,请勿见怪...

    return function() {
    return this.name;
    }
    这里的this代表调用方法时所在的的作用域:全局作用域window。

    return function() {
    return that.name;
    }
    object.getNameFunc()()此处实现了从外部调用局部变量的方法,that会顺着作用域链向上级作用域查找,所以获得是getnameFunc的定义的变量var that = this。

    @小彘:

    是引用类型。f3和f4的值是指针,只想obj()。所以全等是true。

    后两个应该this的作用域问题,不是闭包的问题吧。 弄的我都糊涂了... 还好找了一篇通俗的问题在,终于弄懂了

    执行过f1这个函数后,test如果是局部变量会被销毁,如果是全局变量则会保存在内存堆(heap)里,如果不执行f1函数,test和nadd都是不存在的

    不,我补充一下,我应该是说错了,既然f2函数是闭包,那么f2函数会保持它的外部函数f1的作用域,即使定义test为f1函数的局部变量(var test = 10);那么也要先检查f2是否对test产生引用,如果引用数为0,则在f1()调用完后销毁,再运行f1()()(实际上是调用f2函数)也不会产生对test这个局部变量的引用,所以它就在栈中被销毁了。
    如果理解的不对,请大侠们拍砖

    最后的思考题如果能理解这句话“this的指向是由它所在函数调用的上下文决定的,而不是由它所在函数定义的上下文决定的”,我想就能它们的运行结果了

    this的指向是由它所在函数调用的上下文决定的,而不是由它所在函数定义的上下文决定的。喜欢这句话,但是闭包还是没有理解透彻。

    我觉得大家说了这么多,都没人把代码写出来运行一下来验证自己的答案么,也不管自己的答案是否正确么。通过运行发现:
    思考题一:打印:空白
    思考题二:打印:My Object
    虽然我不太了解,但是评论中的很多人自己也理解错误了。

    R兄講的非常簡單易懂。

    我觉得是这样的,
    第一个里边的this指向的是那个闭包函数,
    第二个例子里边的this指向的是object。

    引用陈守川的发言:

    我觉得大家说了这么多,都没人把代码写出来运行一下来验证自己的答案么,也不管自己的答案是否正确么。通过运行发现:
    思考题一:打印:空白
    思考题二:打印:My Object
    虽然我不太了解,但是评论中的很多人自己也理解错误了。

    第一个 打印结果为 The window
    第二个 打印结果为 My Object

    第一个 this为全局对象,所以alert处理的name为The window
    第二个 that 为object对象,所以alert 处理的name为My object
    第二个好理解,因为在调用前用that 保存了object 自己的this,所以that 就成为是有变量,在闭包内可以调用。
    第一个有点晕,因为第一个的this指向自己,然后被window 给调用this 就成了window对象,它的name就是全局的name


    引用陈守川的发言:

    我觉得大家说了这么多,都没人把代码写出来运行一下来验证自己的答案么,也不管自己的答案是否正确么。通过运行发现:
    思考题一:打印:空白
    思考题二:打印:My Object
    虽然我不太了解,但是评论中的很多人自己也理解错误了。

    这篇文章流传很广,看了多次,忍不住要问了。下面这句话:
    ---------
    Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
    -----------
    请问有哪种语言是“不”可以在函数内部直接读取全局变量的? 全局变量什么意思,就是在任何地方都可以读取。

    引用忍不住要问了的发言:

    请问有哪种语言是“不”可以在函数内部直接读取全局变量的? 全局变量什么意思,就是在任何地方都可以读取。

    我觉得这句话可以改成:
    Javascript语言的特殊之处,就在于函数内部可以直接声明(不使用var)并读取全局变量。

    引用忍不住要问了的发言:

    这篇文章流传很广,看了多次,忍不住要问了。下面这句话:
    ---------
    Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
    -----------
    请问有哪种语言是“不”可以在函数内部直接读取全局变量的? 全局变量什么意思,就是在任何地方都可以读取。

    同感啊,一直对此很费解

  • 相关阅读:
    HashMap 统计一个字符串中每个单词出现的次数
    iOS .a静态库的制作及使用
    iOS framework静态库中使用xib和图片资源详解
    iOS 工程套子工程,主工程和framework工程或.a library静态库工程联调
    iOS 最新framework和.a静态库制作及使用全解(含工程套工程,多工程联调)
    iOS9新特性 3DTouch 开发教程全解(含源码)
    iOS GCD NSOperation NSThread等多线程各种举例详解
    Mac Beyond Compare 永久试用
    cocoapods 常见问题
    iOS 常用工具库LFKit功能介绍
  • 原文地址:https://www.cnblogs.com/rswl/p/7991559.html
Copyright © 2020-2023  润新知