一、prototype
1、prototype属性的作用
JavaScript的每个对象都继承“原型”(prototype)对象上的所有属性和方法。只有null除外,它没有自己的原型对象。
构造函数生成实例对象时,会自动为实例对象分配原型对象。每一个构造函数都有一个prototype属性,这个属性就是实例对象的原型对象。
function Animal (name) {
this.name = name;
}
Animal.prototype.color = 'white';
var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');
cat1.color // 'white'
cat2.color // 'white'
//构造函数Animal的prototype对象,就是实例对象cat1和cat2的原型对象。
//在原型对象上添加一个color属性。结果,实例对象都能读取该属性
原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上
Animal.prototype.color = 'yellow';
cat1.color // "yellow"
cat2.color // "yellow"
如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。
cat1.color = 'black';
cat2.color // 'yellow'
Animal.prototype.color // "yellow";
总结:原型对象的作用,就是定义所有实例对象共享的属性和方法;
实例对象可以视作从原型对象衍生出来的子对象
2、原型链
对象的属性和方法,有可能是定义在自身,也有可能是定义在它的原型对象。由于原型本身也是对象,又有自己的原型,所以形成了一条原型链(prototype chain)。所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性指向的那个对象。
Object.getPrototypeOf(Object.prototype)
//null
//上面代码表示,Object.prototype对象的原型是null,
//由于null没有任何属性,所以原型链到此为止。
//如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”
//“原型链”的作用是,读取对象的某个属性时,JavaScript引擎先寻找对象本身的属性,
//如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。
//如果直到最顶层的Object.prototype还是找不到,则返回undefined。
//如果寻找某个不存在的属性,将会遍历整个原型链。
3、constructor属性
prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。
function P() {}
var p = new P();
p.constructor// function P() {}
p.constructor === P.prototype.constructor// true
p.hasOwnProperty('constructor')// false
constructor属性的作用,是分辨原型对象到底属于哪个构造函数。
function Super() {}
function Sub() {
Sub.superclass.constructor.call(this);
}
Sub.superclass = new Super();
//Super和Sub都是构造函数,在Sub内部的this上调用Super,就会形成Sub继承Super的效果
下例中,a是A的实例。修改了A.prototype以后,constructor属性的指向就变了,导致instanceof运算符失真。
function A() {}
var a = new A();
a instanceof A // true
function B() {}
A.prototype = B.prototype;
a instanceof A // false
所以,修改原型对象时,一般要同时校正constructor属性的指向。
C.prototype.method1 = function (...) { ... };
//只在原型对象上添加方法,这样可以保证instanceof运算符不会失真。
通过name属性,可以从实例得到构造函数的名称
function Foo() {}
var f = new Foo();
f.constructor.name // "Foo"