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

浅谈JS __proto__与prototype的联系与区别

2017-04-07 01:31 393 查看
学习JavaScript也有一段时日了,今天想谈一下JS中一个比较重要的关键概念——原型(prototype)

什么是原型?

W3School中是如此描述原型的:

“Every JavaScript object has a prototype. The prototype is also an object.

All JavaScript objects inherit their properties and methods from their prototype.”

我个人对这段话的理解是,原型prototype是每一个JS对象都有的一个属性,而这个属性是一个对象,所有JS对象都能从它们的prototype继承方法和属性。通过prototype,JS得以以多种方式实现面向对象中的继承概念。

原型继承链

JS的对象之间存在着一个叫做原型继承链的关系。对象能够从自己的prototype继承属性和方法,当对象需要调用不属于自己的方法或是使用不属于自己的属性的时候,对象则会通过一个叫做__proto__(这是什么我们随后解释)的属性去寻找它的prototype,在其中寻找方法和属性,而如果在它的prototype中没能找到的话,又会去它的prototype的prototype中寻找方法和属性,直到达到这条原型继承链的终点为止。

关于这一点,W3School中的解释如下:

“All JavaScript objects inherit the properties and methods from their prototype.

Objects created using an object literal, or with new Object(), inherit from a prototype called Object.prototype.

Objects created with new Date() inherit the Date.prototype.

The Object.prototype is on the top of the prototype chain.

All JavaScript objects (Date, Array, RegExp, Function, ….) inherit from the Object.prototype.”

__proto__和prototype的联系与区别

接下来就要讲到__proto__和prototype的联系和区别何在了。首先,__proto__是什么?

__proto__是JavaScript中对象实例所拥有的一个属性,它指向的是该对象实例所对应的prototype对象。(需要注意的是,如上所述,Object.prototype是所有对象继承的顶点,而它的__proto__的值为null)

__proto__与prototype的区别则在于,__proto__是对象固有的属性,它指向的是该对象实例的构造函数的prototype(Object.prototype除外),而prototype是函数固有的属性,其值是一个函数的原型对象。举个例子:

function Test() {}
var t = new Test();
console.log(t.__proto__ === Test.prototype)//true
console.log(t.prototype)//undefined


此处,Test是一个函数,而t是通过Test构造出的Test的实例,t.__proto__指向的是Test.prototype,而t本身是没有prototype这个属性的。

用一个简单的例子也许还不能说清楚__proto__和prototype的联系,那么我们来看一张我珍藏许久的关于原型继承链的图片吧:



Function Foo() {}
var f1 = new Foo();


下面我们来解释一下这张图。

如图,Foo是一个Function类型对象,f1是通过Foo构造出的Foo的一个实例。

接着,如图所示,f1.__proto__指向的是Foo.prototype,同时,Foo本身的prototype属性自然也是指向Foo.prototype的(废话),这与前面Test和t的例子是一样的。图中还有一个叫做constructor的属性,这个属性是prototype本身固有的一个属性,它的意义是指向构造函数本身。

接着往下看,前面我们说过,prototype本身也是一个对象,它实际上也是一个对象实例,那么它自然也是有__proto__属性的,在此图中,Foo.prototype.__proto__指向的是Object.prototype,这也与我们前面引用的W3School的解释吻合:

“Objects created using an object literal, or with new Object(), inherit from a prototype called Object.prototype.

The Object.prototype is on the top of the prototype chain.”

Object.prototype是继承链的顶点,所有对象除了函数以及通过Object以外的函数构造出的对象实例以外,它们的__proto__属性都默认指向Object.prototype。

接着我们看Foo函数本身,Foo函数本身其实可以看做是通过Function()这个构造函数构造出的一个function实例,因此,Foo.__proto__指向的是Function.prototype;那么Object呢?Object其实也是一个函数,它也同样可以看做是Function的一个实例,所以,Object.__proto__也是指向Function.prototype的。

然后最特殊的则是Function这个函数,一方面,Function的prototype属性指向的当然是Function.prototype;另一方面,Function.prototype是一切函数的原型对象,而Function也是一个函数,也就是说,它是本身的一个实例,(自我感觉很抽象很不可思议,不过JS就是设计出了这样一个函数对象……..)所以它的__proto__指向的是Function.prototype。

最后,Function.prototype也不例外,它是Object的一个实例,它的__proto__属性指向的也是Object.prototype。

上面这么一大段话下来,是不是有点绕?那么来总结一下这张图反映出的继承链关系,假设有如下的代码:

Foo.prototype.testValue = 2;
Object.prototype.method = function() {
console.log('the method inherited from Object.prototype');
}
f1.testValue;//2
f1.method();//the method inherited from Object.prototype
f1.testTwo//undefined


这段代码的执行过程如下:

当要使用f1.testValue时,在f1中没有这个属性,于是通过f1的__proto__到Foo.prototype去寻找testValue,找到以后,得以使用;

同理,在下一行调用method方法,f1中也没有这个方法,于是又通过f1.__proto__去寻找,这次在Foo.prototype中也没有method方法,于是再通过Foo.prototype.__proto__到下一级Object.prototype中去寻找method,找到以后,结束遍历;

再往下,testTwo属性在任意一级的prototype中都找不到这个属性,于是得到了undefined的结果。

总结:

1.__proto__是对象固有的属性,它指向的是该对象实例的构造函数的prototype(Object.prototype除外)。

2.prototype是函数固有的属性,其值是一个函数的原型对象。

3.__proto__与prototype的联系在于:当一个对象要查找自己没有的属性或者方法时,它是通过__proto__属性去查找,而不是通过prototype属性,对象实例本身是没有prototype属性的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: