深谈那些不为人知的闭包问题

闭包

刚刚开始我发现闭包只是函数附带知识的一个小儿科,不就是所谓里面的能用外面的吗?而当我闲来无事,仔细推敲,发现了他的那些深藏的密码。

闭包的定义(很官方)

闭包是指有权访问另一个函数作用域中的变量的函数。(抄书,后面最讲哪本书)

闭包的常规理解

我是个比较喜欢偷懒的人,那么所有代码会一目了然,十分简单,但直达核心。

			function  a() {
            	var i = 1;
            }
            function b() {
                var j = i;
                return j;
            }

            return b();
        }
        //a()结果自然是1

这种东西大家都懂我也不多说。

闭包的几个重要概念

几个重要的概率还是得灌输一下,不然肯定会有新人会听不懂,当然我不是说听不懂我讲的东西,大家都是大智慧者,我怕大家听不懂我的一些概念名称,毕竟不同的书有不同的叫法。

  • 执行环境:简单来说就是函数或变量在执行的过程中,有权访问其他数据,执行环境中所有定义的变量和函数都保存在变量对象中。

  • 作用域链:简单来说就是保证对执行环境中的变量对象的有序访问。既然说到是有序,那么顺序就是作用域链中的下一个变量对象来自外部环境,在下一个变量对象就来自下一个外部环境。
    直接上例子,文字哪有代码美:

         var color = "blue";
         
         function changeColor() {
             var color = "red";
    
             return function (){
                 return color;
             }();
         }
         //结果为changColor()为"red"
    

其中匿名函数的作用域链就是:

序数变量对象
2全局变量对象(color = “blue”)
1changeColor()的活动对象 (color = “red”)
0闭包的活动对象 (其中默认的一般是arguments和this,这个咱不谈)

开始肝正题

咱都了解了一些重要概率那么前戏就做足了,现在开始上床,不, 是上船了,通往闭包深意的小船。

直接上代码:

            function a() {
            	var result = new Array();
            	
            	for (var i=0; i < 10; i++) {
                	result[i] = function () {
                    	return i;
                	}
           	 	}
            
          	    return result;
      	   }

还是熟悉的a( ),在此处,第一层的人思想还停留在返回的是一个 0, 1, 2…9的数组了 ,其实返回的是个函数数组,人家保存的是函数对象,放在那里没有执行,等你让它执行才执行,就像某些学生老师让你写作业你才写,当然我就是这种学生了,第一层的人呢还是好好拜读下对象这章吧,什么你说学的是C语言,没有对象这章,那打扰了。第二层的人思想已经知道了保存的是函数数组,但是呢顶多觉得 a()[0] () = 0,第二层的人说明对这门语言很了解了,但是对闭包还不太了解啊。那么聪慧的博主我呢自然是在第五层了,对老千层饼了。我先公布答案,a( )这个函数数组中所有函数返回值都是10

首先我们讲过匿名函数中肯定是保存了a( )活动对象了,i 就是了,有人问既然都是 i 了怎么不是从0到9的数组呢,此 i 非彼 i ,我这里的 i 其实是个对象,没错就是对象,一个变量对象,不管你都是String还是Integer还是Boolean在我JavaScript都是变量对象。对象啊,一想到对象我就想到了引用,所以呢所有函数数组中的函数都引用这个变量对象 i ,那么我们想想当a( )函数返回后i 变成了多少,没错就是10,所以我们引用的这个 i ,就是a( )中的 i ,即10

既然如此我们如何避免这个问题呢?传闻东方朔好色,每次娶了媳妇,一年期满后,便付分手费打发掉,然后再娶。如果有一个客栈,东方朔老婆想住一晚,但是身上又没带钱,但是客栈的老板知道她是东方朔最近才娶的老婆,于是就让她住了一晚,以后再去府中讨要费用。然后呢老板却忘记讨要,一年后才想起有这回事,所以去东方朔府中讨要,直接表明东方朔老婆欠一晚的客栈钱,但东方朔早就换了老婆,那自热无法对质了,只能不了了之。又有一次,东方朔老婆来客栈还是没带钱,但是老板已经吸取了教训,就叫东方朔老婆把身份信息留在小本本上。过了一段时间,东方朔果然换了老婆,但跟老板没关系,他凭着小本本上的身份信息找到了东方朔的前任老婆,讨回来客栈钱。

于是呢直接上代码:

            function a() {
            var result = new Array();

            for (var i=0; i < 10; i++) {
                result[i] = function (num) {
                    return function () {
                        return num;
                    }(i);
                }
            }

绕绕弯子我们就能得到函数a( ) 装的就是function ( ) { return num; }这玩意,所以此函数的执行环境中记录了对外部匿名函数num的引用,而每次num又记录了 i 的值,故而可以得到每次的i值这样一来,result数组中每个函数都有自己num变量的一个副本,就可以返回各自不同的数值了。

闭包与this对象

  • this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。

首先明白一个知识点:匿名函数的执行环境具有全局性,因此其this对象通常指向window。
直接上代码:

       var name = "The Window";
       
        var object = {
            name : "My Object",

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

        alert(object.getName()());
        //答案是 "The Window"

其实每个函数在被调用时都会自动取得两个特殊变量:thisarguments。内部函数在搜素这两个变量只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。其实这都是书上的解释,太高级了,换个简单的。
getName()这个函数是Object这个对象在使用的方法,据this的定义,妥妥的this = Object,当此函数返回的匿名函数执行时,在他的执行环境中左看右看,左找又找,没找到自己的主人,自己就是个无主的函数执行,所以调用自己的主人就是全局了,也就是window了。这样返回的值就是window.name自然是"The Window"。

至于解决的方法,万变不离其宗,无疑拿个小本本记下来。

        var name = "The Window";
        var object = {
            name : "My Object",

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

        alert(object.getName()());
        //"The Object"

聪明人从来不讲方法一样的习题,有缘读者自悟。

接下来我们继续探讨下这个this问题。
上代码:

        var name = "The Window";
        var object = {
            name : "My Object",

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

        alert(object.getName()());
        
        object.getName(); //"My Object"
        (object.getName = object.getName)(); //"The Window"

第一个式子显然易见,主要是第二个式子。有人肯定在这里要蒙,这是什么写法这能通过编译?我的回答是当然,如果你觉得不是,那么请翻开书的运算符这一章温习温习,别说你C语言这也没有。我们写一个简单的表达式:a = b = c(放心所有变量都赋值过)。这是一个超级正常的式子,没人觉得这也编译不出来吧,那么可以解释第二个式子可以编译了吧,你说你没听懂?=的结合性是从右至左,也就是 a = (b = c)说明啥,(b = c)是一个值,这里又有人要问了什么其实(b = c)是变量b,说它是个变量不是值,真实情况是c赋值给b,变成 a = b,然后b赋值给a,我知道有很多人都是这种观点,甚至有的老师讲解的时候也是这个观点。我抱歉,他就是一个值,是一个常量或者是一个对象,但就是不是变量。口说无凭上代码。
从在这里插入图片描述
十分简单的例子,却有效说明(c = a)并不是一个左值,所以它绝对不是一个变量,那么他就是常量5,5 = 3,瞬间出错。

搞清楚这个问题,第二个式子就容易理解了,式子(object.getName = object.getName)其实就是个函数本身,一个函数本身就单纯是函数而不是一个方法,自然没有主人,没主人那么window就收咯。

收尾

所以今天关于闭包的分享就到这里了,其实这里面一大半代码都没有啥实际意义,就是单纯的分享思想,闭包的思想,如果有人觉得这些代码有实际意义,那么说你的代码写的太垃圾了,代码写成这样,不简洁又带着混乱,混乱又带着迷惑,迷惑又带着点点蛋疼,所以我们只讨论闭包的思想,不讨论代码的实际意义。鄙人也是闲来无事抛砖引玉,程序语言更像是艺术品,需要仔细品尝,但又需要实际使用,总没有人用菜刀的时候关心上面的刻画吧,人们只管切的快就好。

感谢

其中大多数知识都来自于《JavaScript高级程序设计》中第7章函数表达式的第2节闭包中的内容,很多定义和例子引用其中,若是感兴趣的人可以买来读一下。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值