js 原型链实例解析

废话不多说,直接上干货!

var test = (function(){
    var that;
    var obj = function(){
        this.name = "name";
        that = this;
    }
    obj.prototype = {
        aaa: function(){
            console.log("this is aaa!");
            console.log(this,that,test);
        },
        bbb: function(){
            console.log("this is bbb!");
            that.aaa.call(this);
        }
    }
    return new obj;
})()

分析一下这个函数包含的知识点:立即执行函数、闭包、原型链、继承、封装、call / apply 的应用等等。接下来我将一点点说一下我的理解,如果说的有不对之处,还请大神指教。

先说好处:

1、只定义了一个全局变量,能减少全局污染。

2、把子函数写进了原型(prototype),方便继承调用。

3、函数看起来干净整洁,可扩展,可相互调用;

4、想不出来了……

从立即执行函数说起:

立即执行函数都干了什么事?实例化 obj 函数对象,并赋值给了 test 变量,所以实际上是 test 变量实例化了 obj 对象。

那 new 都干了什么事?简单的说,初始化一个对象、实现继承;

初始化一个对象我就不说了,就是给一个对象赋予一个预定义好的值;

实现继承:

test 变量的值是 new obj 后赋予的,test 继承了 obj 的属性(如:this.name)及 obj.prototype

怎么验证 test.__proto__ === obj.prototype?改版一下上面的代码

var obj = function(){
    this.name = "name";
}
obj.prototype = {
    aaa: function(){
        console.log("this is aaa!");
    },
    bbb: function(){
        console.log("this is bbb!");
    }
}
var test = new obj();

运行代码,在控制台测试:

再说的深一点:obj 是 Function 对象得来的,证明一下:

再深一点:

再深一点:

OK,到头了,原型链的尽头,这也就是我们在 js 中常说的 ”一切皆对象“!  【斜眼笑】

回归正题:再说下那个 that :

这个 that 首先实现了闭包(背一下面试题答案:内层函数用到外层函数的变量就是闭包!)

再加一行代码:(看注释部分)

var test = (function(){
    var that;
    var obj = function(){
        this.name = "name";
        that = this;
    }
    console.log('我是that:' + that);     //这里是注释
    console.log('我是this:' + this);     //这里是注释
    obj.prototype = {
        aaa: function(){
            console.log("this is aaa!");
        },
        bbb: function(){
            console.log("this is bbb!");
        }
    }
    return new obj;
})()

呵呵!

但是没错,js 是顺序执行的,到输出 that 的时候,that 只是定义的,并没有赋值,所以是 undefined;当然,this 的指向取决于调用函数的对象,目前谁调用了 this ?没有!那就是 window。

然后,想看 that 是什么鬼,再动动代码:

var test = (function(){
    var that;
    var obj = function(){
        this.name = "name";
        that = this;

        console.log( that );     //这里是注释
        console.log( this );     //这里是注释
    }
    obj.prototype = {
        aaa: function(){
            console.log("this is aaa!");
        },
        bbb: function(){
            console.log("this is bbb!");
        }
    }
    return new obj;
})()

看下结果:

哈哈,有什么发现吗?是不是跟上面输出的 test 一模一样?

好吧,重新捋一下思路:test 立即执行函数返回了实例化的 obj ,执行了定义的 obj 函数,实现了继承。(说到这里等于没说)那要那个闭包的 that 有何用?别急,看一下 obj.prototype 这个对象里面的函数,再动动代码

var test = (function(){
    var that;
    var obj = function(){
        this.name = "name";
        that = this;
    }
    obj.prototype = {
        aaa: function(){
            console.log("this is aaa!");
            console.log(this,that,test);
        },
        bbb: function(){
            console.log("this is bbb!");
            that.aaa.call(this);
        }
    }
    return new obj;
})()

test.aaa();    //这里是注释,一口气输出了 this、that、test 三个东西!

看下结果:(先提前呵呵!)

OK,先说 this ,谁调用 aaa 函数?test!那 test 就是它的指向。

再说 that 

that 在实例化的时候被赋予了 this ,这个this return 之前,它自己也不知道指向谁,后来(return 之后),它指向了 test ,也就是说 这时候的 that 实际上就是 test。(其实这样说不严谨,在实例化 new 的时候,this 就注定指向了被实例化后的对象,不论这个对象是谁,只是这里多了一步赋值给 that )

test 本身就不多说了,这个像是递归的调用。

利用上面的代码再执行:

test.bbb();

看下结果:

这似乎看不出来什么,好吧,那就动动代码

var test = (function(){
    var that;
    var obj = function(){
        this.name = "name";
        that = this;
    }
    obj.prototype = {
        aaa: function(){
            console.log("this is aaa!");
            this.xixi = "xixixi";
            console.log(this.haha + this.xixi);
        },
        bbb: function(){
            console.log("this is bbb!");
            this.haha = "hahaha";        //我动了
            that.aaa.call(this);
        }
    }
    return new obj;
})()

test.bbb();    //这里是注释,一口气输出了 this、that、test 三个东西!

看下结果:

想必结果大家都知道,但是我想说的是,这里的 call 是怎么运行的,看代码

var test = (function(){
    var that;
    var obj = function(){
        this.name = "name";
        that = this;
    }
    obj.prototype = {
        aaa: function(){
            // console.log("this is aaa!");
            // this.xixi = "xixixi";
            // console.log(this.haha + this.xixi);
        },
        bbb: function(){
            console.log("this is bbb!");
            this.haha = "hahaha";        //我动了

            // that.aaa.call(this);         //我被注释了

            console.log("this is aaa!");    //下面是挪过来的
            this.xixi = "xixixi";
            console.log(this.haha + this.xixi);
        }
    }
    return new obj;
})()

test.bbb();    //这里是注释,一口气输出了 this、that、test 三个东西!

看看是不是一样的:

这里 call 的作用就是把 aaa 函数的内容拿过来在 bbb 函数里执行,然后还要用 bbb 函数的 this (毫不留情的那种,霸占了地盘还霸占了夫人【斜眼笑】)哈哈。

apply 的用法也是一样的,只是两者参数格式不一样而已,call 可以传多个参数,apply 只有两个参数,第二参数是个数组。

 

OK , 最后照抄大神的代码,用 call 实现一下 jQuery 中的 each 函数

var each = function(array,fn){
    for(var index = 0; index < array.length; index++ ){
        fn.call(array[index], index, array[index]);
    }
}

each([2,3,4],function(i,item){
    console.log(i, item);
});

好了,今天我就学到这么点,都给大家分享了,有什么不对的地方,还请多多指正。

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值