对于 JS 来说,__proto__
和prototype
的区别是个绕不开的话题。本文就试图从它们的根本上说清楚它们是什么,又有什么区别,所以本文会从 JS 的对象开始说起,以其期待把本文的主题说透彻说明白。
一、JS的对象创建方法
大家都知道,在JS 的世界中有一句话:万物皆对象
。而 JS 中的对象跟其他强类型的语言中的对象又有天壤之别,本文无意讨论 JS 中对象跟譬如 C#、java 等强类型语言中的对象的区别,不过还是要说下 JS 中对象创建的方法。
JS 语言本身非常灵活(不严谨),它的对象也非常灵活,比如 JS 允许动态的给对象添加属性(此处的动态添加属性指的是添加之前没有定义过的属性)。JS 创建对象的方式也是很多的,比如要创建一个用来描述“人”的对象p,该对象具有 name 、age 属性和say()方法。那么创建对象的手段至少有如下几种:
- 使用Object创建
var p = new Object();
p.name = "homes";
p.age = 32;
p.say = function(){
console.log("hello");
}
- 使用字面量方式
var p = {
name: "homes",
age: 32,
say: function () {
console.log("hello");
}
};
- 使用构造函数
var Person = function () {
this.name = "homes";
this.age = 32;
this.say = function () {
console.log("hello");
}
}
var p = new Person();
等等等,总之还有很多方法,比如使用原型来创建对象,使用拷贝方式,使用工厂方式。其实这些方式只是形式上有所变化而已。
这里重点要讨论的是第3种,使用构造函数创建对象
二、双对象法则
首先观察使用构造函数创建对象的代码,var Person = function( ){....}
,由于 JS 没有“类”的概念,其实这里的 Person 其实就是个函数;不过在 JS 的世界里还有一句话:万物皆对象,所以函数也是个对象(是由JS内置对象Function实例化出来的),那么以此得出 Person 是一个函数性质的对象。
上面这句话要说明什么呢?
重点来了
,在 JS 中如果一个对象是函数,那么这个对象是由两部分构成的,一个是这个函数的定义部分,如果这个函数被当做构造函数来用了,那么这第一部分就是这个构造函数。
第二部分,是这个函数的原型对象。
很多人在看到“原型对象”这个词的时候彻底懵逼,在这里必须把这个"原型"这个词说明白了。我第一次看到这个词“原型对象”的时候,我就想起了西游记,里边的妖怪最后都要现原形,难不成 JS 的世界就是妖怪的世界?每个对象都有个“原形”?可以通过某种手段让一个 JS 对象现原形?这是大错特错的。
这里的“原型对象”只不过是起个名字而已,我们就这样理解就好了,每一个函数都是对象
,当代码运行时,在内存中每个对象都由2块内容来合成这1个对象;换句话说代码运行时,内存里有2个独立的空间,这两个独立空间存储2个不同的对象,1个是函数体本身,一个是另外一个对象,这2个东西合成我们定义的一个函数对象,而这个“另一个对象”我们起名叫做“原型对象”,仅此而已。
那接下来问题来了
1、那个“原型对象”有什么用?
2、既然一个函数对象是由2部分组成的,那这两部分又是如何联系起来的?
三、__proto__
和prototype
prototype
是什么?
先不管那个额外的“原型对象”有什么鸟用,我们先看看一个函数对象的两部分是如何联系起来的。为什么要回答这个问题,因为如果两部分不能有效的联系起来,当我们 JS 程序中有多个函数对象时岂不是要乱套?
它们两部分之间是通过prototype
联系起来的(prototype
指向的对象就是我们说的原型对象)。看图
如图,我们上边声明了Person构造函数,当程序运行时,内存里会有2个部分,一个是左边的方框,代表该函数体,右边的方框代表该函数的另一部分,即它的“原型对象”。function
的内存中,除了有我们声明name,age属性,还有一个prototype
,这个属性是一个指针,它指向自己的原型对象所在地址。同理,每个原型对象也有一个属性,叫constructor
,也是个指针,它指回了自己的另一半,即该原型所从属的构造函数。
比如,我们可以使用如下语句:Person.prototype,来访问函数自己的原型对象。
需要注意的是,如果这个对象不是函数,那么该对象不具有prototype属性。
例如:
var a = {
name: "homes",
age: 32,
say: function () {
console.log("hello");
}
};
console.log(a.prototype); // 输出:undefined
var b = new Date();
console.log(b.prototype); // 输出:undefined
var c = new String("123");
console.log(c.prototype); // 输出:undefined
即使一个对象是根据构造函数实例化出来的,由于该对象不是函数,它也不具有prototype
属性。例如:
var Person = function () {
this.name = "homes";
this.age = 32;
this.say = function () {
console.log("hello");
}
}
var p = new Person();
console.log(p.prototype); // 输出:undefined
说白了,prototype
属性是只能通过 函数名.prototype
的形式来访问。
__proto__
又是什么?
首先要注意,这个“__proto__
”左右两边是2个连续的下划线。
它是 JS 中所有对象都有的属性,既然如此,函数也是个对象,那么它也必须有,所以上图可以再改改,把这个__proto__
加上去。
如上图所示,不管是函数本身那个框框,还是它的原型对象那个框框,都有__proto__
属性,并且__proto__
属性也是一个指针,它指向“它的构造函数的原型对象”。怎么理解这句话?
在 JS 的世界里,每一个函数都是 Function
对象的实例,也就是通过 new Function()
构造出来的。所以在这里,Person
函数也是由 Function
构造出来的一个实例,函数实例。所以,Person函数的__proto__
指向了Function
函数的原型对象,也就是Function.prototype
。
而 Person 的原型对象 Person.prototype
也是个对象,它也有__proto__
属性,它的__proto__
也要指向“它的构造函数的原型对象”,那么这个 Person
的原型对象的构造函数的原型对象又是谁呢?答案是Object
的原型对象,也就是Object.prototype
。
接下来深入一步,既然Function
的原型对象 Function.prototype
是个对象,它也有__proto__
属性吧,那Function.prototype.__proto__
又指向谁呢?答案是指向 Object
的原型对象,也就是Object.prototype
。
接下来再深入一步,Object.prototype
的__proto__
又指向谁呢?由于Object
对象就是 JS 的世界中的原始天尊了,所以Object.prototype.__proto__
指向null
。
好绕啊,我决定画个图。
请仔细观察此图,这个图里有一个非常诡异的浅蓝色箭头,它描述的信息是Object
的__proto__
指针指向了Function.prototype
。这是几个意思?你刚刚不是说了Object就相当于JavaScript的原始天尊了吗?额,额,好吧,Function对象是原始天尊的爸爸级别的。换句话说Object也是Function搞出来的。。。。
那根据构造函数Person( ) 创建出来的实例呢?
例如 : var p = new Person( );
由Person()函数构造出来的实例p就只是个普通对象了,所以p不是函数,也就是说,p这个对象只有__proto__
属性,没有prototype
属性。p.__proto__
指向“它的构造函数的原型对象”,所以p.__proto__
指向的对象是Person.prototype
,因为p
的构造函数是Person
,它的原型对象就是Person.prototype
。如图。
- 如图,虚线表示实例化的意思,
p
是由Person()
构造函数实例化出来的。由于p
不是函数,所以p
没有它自己的"原型对象"。
总结:
每个对象都有__proto__
属性,它指向该对象的构造函数的原型对象。
函数是一种特殊的对象,它由2部分构成,一部分是函数体本身,一部分是它的原型对象
函数除了有属性__proto__
,还有属性prototype
,prototype
指向该函数的原型对象。
到这里我们把__proto__
和prototype
基本说明白了,那么最后又引申出一个问题:这个该死的原型对象有什么用?
参考文章:
- JavaScript prototype protojavascript prototype proto prototype之间proto prototype proto javascript prototype mutation tosorted javascript toreversed prototype mutation javascript prototype tospliced mutation javascript prototype class and 原型javascript prototype javascript prototype 25 javascript prototype 13