代码改变世界

javascript 闭包的理解(二)

2019-07-10 00:40  盛世游侠  阅读(316)  评论(0编辑  收藏  举报
    // 定义一个User构造函数
    function User(properties){
        //遍历对象属性,确保它作用域正确
        for(var i in properties){
            (function(which){
                var p = i;
                //为属性创建获取器
                which["get"+i] = function(){
                    return properties[p];    
                };
                //为属性创建设置器
                which["set"+i] = function(val){
                    properties[p] = val;    
                    //return properties[p];
                };
            })(this);
        }
    }
    
    // 创建一个User实例
    var user = new User({
        name:"Bob",
        age:44,
        islive:true,
        money:50000,
        earn:5000,
        nation:"china",
        job:"web Dev"    
    });


        user.getname();           // 返回 "Bob"
        user.getage();              // 返回 "44"

        user.getage.call(window);      // 依然返回44
        user.getage.call({age: 23});  // 还是返回44

上面的这段代码(我们叫它代码段A吧)里, user.getage.call(window)user.getage.call({age: 23}) 返回的结果,虽然在意料之中,但是我却并不非常清楚的知道为什么是这样。

 

我只是大概的知道,之所以是这个结果,肯定跟User构造函数里的闭包有关系。

我认为User构造函数里有两个闭包,所以我猜想调用user.getage的时候,就生成了2个闭包作用域。

但这只是我的猜想,事实是不是这样的呢?我想到了一个办法来验证:使用console.dir()

var getage = user.getage;

console.dir(getage);

输出如下:

从输出的对象结构,可以看到这个方法它的[[Scopes]]确实有两个Closure,而且每个Closure里面有些什么东西也显示出来了。

 

我猜想在执行getage方法时,会先执行第一个闭包作用域,也就是Closure 0,这个闭包是由代码段A里的以下代码生成的:

which["get"+i] = function(){
    return properties[p];    
};

我觉得这里应该搞清楚闭包、闭包作用域、闭包包含的作用域 三个概念,否则理解起来会很蒙,搞不清楚。

闭包:就是以上代码所定义的函数which[“get”+i]。

闭包作用域:就是由以上代码生成的执行上下文空间,它就是对象结构图中的那个[[Scopes]]。

闭包包含的作用域:可以看到[[Scopes]]里面还包含了getage闭包引用的另外两个闭包的作用域里的变量:立即执行函数的作用域里的变量p 和 User函数作用域里的变量i变量properties

 

从对象结构里可以看到,闭包 Closure 0 里面存在一个变量p,因此执行闭包时,会去闭包作用域里搜索p,但搜索不到,因为p是在立即执行函数的作用域里定义的,所以会立即进入搜索Closure 0作用域的流程。

搜索Closure 0时,找到了p,并且发现p = i,此时p的定义找到了,但p等于i,所以进入搜索变量i的定义的流程。

 

因为变量i没有在立即执行函数里定义,所以Closure(User)被保留了下来,在立即执行函数的作用域里找不到变量i,就进入Closure(User)作用域寻找,找到了i,同时应为是立即执行函数,所以i的值一搜索到,就理解返回给p,并存入Closure 0作用域中,而Closure(User)中的i的值由于程序继续执行而变化,最后只会存入最后设置给i的值。

 

properties的值也是一样,在闭包作用域里找不到,就会去立即执行函数的作用域里找,找不到再去Closure(User)里找,找到了立即返回。

 

所以上面那段生成闭包 Closure 0 的代码执行后,返回的结果就是 properties[i],此时闭包作用域使用完毕,不再需要,可以回收,进入解析Closure(User)作用域的步骤

然后properties[i] 被解析为 properties[“age”],然后被返回。