您的位置:首页 > Web前端 > JavaScript

我对js原型链的理解

2016-04-26 02:55 633 查看
引言

从关键字 new 说起

__proto__与prototype

Function与Object

结尾

引言

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 作为原型链的一个出口。

结尾

希望能对读者朋友们有所启发,有想法请留言与我交流,转载请注明作者和地址,谢谢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: