我们先用构造函数生成一个对象:
function Person() {
}
var person = new Person();
person.name = "ze";
console.log(person.name) // ze在这个例子中,Person就是一个构造函数,我们用new Person创建了一个实例对象。
prototype
每个函数都有一个prototype属性,注意prototype是函数才有的属性:
function Person() {
}
Person.prototype.name = "ze";
var person1 = new Person();
var person2 = new Person();
console.log(person1.name) // ze
console.log(person2.name) // ze那么这个函数的prototype属性到底指向什么,是函数的原型么。
其实,这个prototype的指向是一个对象,这个对象正是调用该构造函数而创建的实例的原型,也就是这个demo中person1和person2的原型。
那么什么是原型,原型意味这什么,又有什么用途。可以这样理解:每个js对象,除null,在创建创建的时候就会关联另一个对象,这个对象就是我们所说的原型,每个对象都会从"原型"上继承属性。
__proto__
与此同时,每一个对象,除null,都有一个属性,__proto__,这个属性会指向该对象的原型。
function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true于是我们更新关系如下:
实例对象和构造函数都有东西指向原型,原型是否有东西指向他们呢。
constructor
指向实例的倒是没有,你可以用构造函数新建多个实例,原型怎么指得过来。但是原型是有指向构造函数的,那就是constructor,原型的constructor指向构造函数。
function Person() {
}
console.log(Person === Person.prototype.constructor); // true于是可以更新关系图如下:
现在可以得出:
function Person() {
}
var person = new Person();
console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
//一个ES5的方法,可以获得对象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true实例与原型
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。这也就构成了我们是所说的原型链。
补充
当获取 person.constructor 时,其实 person 中并没有 constructor 属性,当不能读取到constructor 属性时,会从 person 的原型也就是 Person.prototype 中读取,正好原型中有该属性。
最后是关于继承,前面我们讲到“每一个对象都会从原型‘继承’属性”,实际上,继承是一个十分具有迷惑性的说法,引用《你不知道的JavaScript》中的话,就是:
继承意味着复制操作,然而 JavaScript 默认并不会复制对象的属性,相反,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。