其实说__proto__并不准确,确切的说是对象的[[prototype]]属性,只不过在主流的浏览器中,都用__proto__来代表[[prototype]]属性,因为[[prototype]]只是一个标准,而针对这个标准,不同的浏览器有不同的实现方式。
对象都有__proto__(或者[[prototype]]),而prototype
属性是只有函数对象才特有的属性,当你创建一个函数时,js
会自动为这个函数加上prototype
属性,值是一个空对象。即非函数对象只具有属性__proto__,可称为隐式原型,该对象的隐式原型(__proto__)指向构造该对象的构造函数的原型(prototype),这也保证了实例能够访问在构造函数原型中定义的属性和方法。原型对象也有一个属性,叫做constructor,这个属性包含了一个指针,指回原构造函数。
function Person(name, age) {
this.name = name;
this.age = age;
}
var person1 = new Person('jessica', 27);
当我们new Person()
的时候到底发生了什么?
new
一个构造函数,相当于实例化一个对象,这期间其实进行了这三个步骤:
-
创建对象,设为o,即:
var o = {}
; -
上文提到了,每个对象都有
__proto__
属性,该属性指向一个对象,这里,将o
对象的__Proto__
指向构造函数Person
的原型对象(Person.prototype
); -
将
o
作为this
去调用构造函数Person
,从而设置o
的属性和方法并初始化。
当这3步完成,这个o
对象就与构造函数Person
再无联系,这个时候即使构造函数Person
再加任何成员,都不再影响已经实例化的o
对象了。
此时,o
对象具有了name
和age
属性,同时具有了构造函数Person
的原型对象的所有成员,当然,此时该原型对象是没有成员的。
js在创建对象的时候,都有一个叫做__proto__
的内置属性,用于指向创建它的函数对象的原型对象prototype
对象创建方式:
a:对象字面量
var Person = {
name: 'jessica',
age: 27
}
这种形式就是对象字面量,通过对象字面量构造出的对象,其__proto__
指向Object.prototype
。
所以,其实Object
是一个函数也不难理解了。Object、Function都是是js自带的函数对象。
b:构造函数
function Person(){}
var person1 = new Person();
c:Object.create
var person1 = {
name: 'jessica',
age: 27
}
var person2 = Object.create(person1);
这种情况下,person2
的__proto__
指向person1
。在没有Object.create
函数的时候,人们大多是这样做的:
Object.create = function(p) {
function f(){};
f.prototype = p;
return new f();
}
总结
其实仔细思考下上面提到的三种创建对象的方法,追究其本质,不难发现,最根本的还是利用构造函数再通过new
来创建对象。所谓的对象字面量也只不过是语法糖而已,本质上是var o = new Object(); o.xx = xx;o.yy=yy;
。
js在创建对象的时候,都有一个叫做__proto__
的内置属性,用于指向创建它的函数对象的原型对象prototype
原型链
原型链的基本思想就是利用原型让一个引用类型继承另一个引用类型的属性和方法。
让我们再简单回顾下构造函数、原型和实例的关系:
每个构造函数(Person())都有一个原型对象(prototype),原型对象(prototype)包含一个指向构造函数(Person())的指针(constructor
),而实例则包含一个指向原型对象(prototype)的内部指针(__proto__
)。
js
在创建对象的时候,都有一个叫做__proto__
的内置属性,用于指向创建它的函数对象的原型对象prototype
。只有函数有prototype
, 当你创建一个函数时,js
会自动为这个函数加上prototype
属性,值是一个空对象。