我对js原型链的理解

引言

js原型链是js面向对象编程的基础和重点,许多文章都对它进行了讲解,这里我想谈谈我对原型链的理解,一方面加深自己的印象,另一方面希望能和大家分享交流。

从关键字 new 说起

我来试着模拟一下new的操作过程。

//模拟new关键字的行为
function methodNew( func ) //func:新对象的构造函数
{
    if ( func === Object )
        return Object(); //Object()方法返回原始的object对象
    else
    {
        var obj = {}; //{}实际上等价于Object()

        obj.constructor = func; //更改obj的constructor属性

        obj.__proto__ = func.prototype; //设置obj.__proto__属性,从而访问到原型链上的属性,
        //比如 func.prototype.name可以通过 obj.name读取,但是不能使用obj.name来修改,这样会为obj添加一个name属性。

        //这里只处理func方法参数为空的情况
        func.call( obj ); //为obj添加func方法中定义的属性,比如
        //function func(){ this.name = "Jerry" }
        //相当于执行obj.name = "Jerry"                   
        return obj;
    }
}

来测试一下

function People()
{
    this.name = "杨幂";

    this.introduce = function()
    {
        alert("My name is "+ this.name );
    }
}
    var obj = methodNew( Object );
    var beauty = methodNew( People );   
    console.log( obj ); 
    console.log( beauty );

结果如下

Object {}
People {constructor: function, name: "杨幂", introduce: function} 

结果与new关键字生成的对象一致。
稍微总结一下,new关键字的行为与后面的方法是不是 Object() 有关,
非Object() 方法时,执行下面四步

1. var obj = {}; //生成一个object对象
2. obj.constructor = func; //设置constructor,由此判断对象的类型由constructor属性决定
3. obj.__proto__ = func.prototype; //这一步很关键,将obj连入原型链,从而能访问func.prototype对象以及它的原型链上端对象的属性
4. func.call( obj ); //为obj添加func()方法中声明的属性

__proto__prototype

这里我把__proto__放在前面,因为它是原型链的基础。

People.prototype.hobby = "篮球";
People.prototype.swim = function(){ console.log( "swimming" ) };
var obj = {};
obj.__proto__ = People.prototype;
console.log( obj.hobby );
obj.swim;

对象访问属性的顺序是先查找自身,显然 hobbyswim 都不是obj自身的属性,这是系统会查找obj.__proto__指向的对象是否有这两个属性,这里就是 People.prototype ,如果还没有,会继续找People.prototype.__proto__指向的对象有没有所需属性,这里有一个知识点,声明一个函数People()时,People.prototype 属性指向谁呢?
根据我的研究,函数的prototype属性的初始化很简单,分为两步

 1. People.prototype = {}; //将一个object对象赋给prototype
 2. People.prototype.constructor = People; //设置constructor为People,表示原型对象的类型是People

因为People.prototype是一个普通的object对象,所以有
People.prototype.__proto__ 指向 Object.prototype ,
这与图中的描述是吻合的。
如此一来,下面的原型链就形成了
obj  __proto__  People.prototype  __proto__  Object.prototype  __proto__  null
有一条对原型链文章的评价:js对象其实就是键值对,说的不错,js中的方法和变量都以对象的形式存在,一个对象能访问到的其实就是 自己的属性原型链上的属性
看看下面的例子

People.prototype.hobby = "篮球";
People.prototype.swim = function(){ console.log( "swimming" ) };
var obj = {};
obj.__proto__ = People.prototype;
//console.log( obj.hobby );
//obj.swim;
obj.hobby = "撩妹";
console.log( People.prototype.hobby );

发现结果仍然是 篮球,说明这里是为obj添加了自己的hobby属性,并没有改动原型对象的属性。
我们再做进一步测试

People.prototype.sing = function(){ console.log( this.song ) }

var obj = {};
obj.song = "爱的供养";
obj.__proto__ = People.prototype;
obj.sing();
People.prototype.sing.call( obj );
//结果
//爱的供养*2

这一步解释了原型对象中的方法是怎么被执行的。



这个图解释了原型链的原理,放在这里供大家参考

FunctionObject

图中 FunctionObject 的关系比较令人费解,其实可以这么理解。
js中所有的对象都是由函数通过new操作符生成的,比如
var obj = new People()
则一定有obj.__proto__ == People.prototype'
而所有的函数对象都是由 Function() 函数生成的,Object()方法也不例外,所以有
Object.__proto__ = Function.prototype
当然,Function()方法也是由自己生成的,所以推出
Function.__proto__ = Function.prototype
(其实Object()方法和Function()方法都是 本地代码(native code),也就是事先写好的,并不是生成的对象,原型链中这样设计是为了逻辑的完备)。
这里比较特殊的两个对象是 function.prototypeobject.prototype,这两个对象的生成不符合上面提到的规律

1. People.prototype = {}; 
2. People.prototype.constructor = People; 

function.prototype 作为Function的原型对象是由Function()函数直接返回的,并不是一个object对象,而且这个属性是只读的,它的值显示如下
function Empty() {}
从逻辑上来说, 所有的函数对象也应该能访问 Object.prototype 的属性,所以有
function.protototype.__proto__ = Object.prototype
让所有的函数对象通过 function.prototype 访问原型链的末端 Object.prototype
作为原型链的尾端, Object.prototype 的属性可以被js中的所有对象访问, Object.prototype__proto__是只读的,这保证了 Object.prototype 作为原型链的一个出口。

结尾

希望能对读者朋友们有所启发,有想法请留言与我交流,转载请注明作者和地址,谢谢!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值