这是最简单粗暴的继承方式。让构造函数 A 的原型对象是构造函数 B 的实例,即 A.prototype = new B()
。这样一来,A 的实例的原型就会指向 B 的实例,A 经过原型链就能访问到 B 的属性和方法。假设 B 的原型又是 C 的实例,即 B 继承了 C,沿着原型链一直向上,就构成了实例与原型的链条,也实现了继承。函数
function SuperType () {
this.property = true;
}
SuperType.prototype.getSuperValue = function () {
return this.property;
}
function SubType () {
this.subproperty = false;
}
/* 继承 SuperType */
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.getSubValue = function () {
return this.subproperty;
}
var instance = new SubType();
console.log(instance.getSuperValue()); /* true */
复制代码
上面的例子中,getSuperValue
定义在 SuperType
的原型中,而 property
定义在了 SuperType
的实例中。随着继承关系的实现, SuperType
的实例成为了 SubType
的原型,property
也就定义在了 SubType
的原型中了。随后,咱们将 SubType
原型的 constructor
修改成 Subtype
,并在其中定义了 getSubValue
方法。SubType
的构造函数还在其实例中定义了 subproperty
属性。工具
当咱们经过 instance
实例访问 getSuperValue
函数时,查找顺序为:instance -> Subtype.prototype(也是SuperType的实例) -> SubType.prototype.__proto__(即SuperType.prototype),最终在 SuperType
的原型中找到了这个函数并调用。优化
这种方式存在与「利用原型模式建立对象」相似的问题:构造函数 SuperType
的实例成为 SubType
的原型,那么 SubType
的原型中存在 SuperType
的实例属性,这些属性将会被 SubType
的实例共享。ui
另外一个问题是,没法向 SuperType
传递初始化参数。this
也叫伪造对象或经典继承,即在子类的构造函数中调用父类的构造函数。究其根源,也就是将父类构造函数定义属性和函数的操做由子类构造函数完成,本来父类构造函数会在父类实例中定义这些属性和函数,如今子类也在本身的实例中定义了这些属性和函数。别忘了,做用域链上,子类会屏蔽对父类的同名属性和函数的访问。spa
function SuperType (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
function SubType (name) {
/* 继承了 SuperType */
SuperType.call(this, name);
this.age = 27;
}
var instance1 = new SubType('Nocholas');
instance1.colors.push('black'); /* colors 是借用 SuperType() 定义在 instance1 上的实例属性 */
console.log(instance1.colors); /* 'red, blue, green, black' */
var instance2 = new SubType('Grey');
console.log(instance2.colors); /* 'red, blue, green' */
console.log(instance2.name); /* 'Grey' */
console.log(instance2.age); /* 27 */
复制代码
若是仅仅是借用构造函数,那么也就没法避免构造函数模式存在的问题 —— 函数都在构造函数中定义,没法复用。prototype
组合继承 = 原型链 + 借用构造函数code
function SuperType (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Super.prototype.sayName = function () {
console.log(this.name);
}
function SubType (name, age) {
/* 继承 SuperType 属性 */
SuperType.call(this, name);
this.age = age;
}
/* 继承 SuperType 方法 */
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
console.log(this.age);
}
复制代码
咱们利用 SuperType.call(this, name)
将 SuperType
的属性定义到了 SubType
的实例中,也就不存在共享属性的问题了;又利用 SubType.prototype = new SuperType()
为 SubType
提供了 SuperType
原型中的函数。这样一来,SuperType
的属性和函数都获得了继承,且属性不被共享、函数是共享的。对象
有一个小细节值得留意,因为 SubType.prototype
是 SuperType
的实例,因此也会存在 name
和 colors
属性,这是咱们使用原型链模式带来的附属产品。只不过因为 SubType
实例中存在同名属性,「屏蔽」了对 SubType.prototype.name
和 SubType.prototype.age
的访问而已。后面咱们还会再优化它。继承
准确的讲,我认为「原型式继承」只是新建了一个原型指向传入对象的空白对象。
function object (o) {
function F () {};
F.prototype = o;
return new F();
}
复制代码
这种继承方法虽然不会做为直接可使用的继承方式,但能够做为其余继承方式的一种工具。千万不要忘记,o 做为原型,其属性是被共享的。
ES5 中规范化了原型式继承 —— Object.create()
方法。它接收两个参数,第一个参数与 object(o)
的参数相同;第二个参数可选,可为返回对象定义新的属性。
寄生式继承的思路与寄生构造函数和工厂函数相似,即建立一个仅用于封装继承过程的函数,该函数在内部以某种方式来加强对象,最后再像是真地是它作了全部工做同样返回对象。
function createAnother (original) {
var clone = object(original); /* 经过调用函数建立一个新对象 */
clone.sayHi = function () { /* 以某种方式来加强这个对象 */
console.log('Hi');
}
return clone; /* 返回这个对象 */
}
var person = {
name: 'Nicholas',
friends: ['Shelby', 'Court', 'Van']
}
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); /* 'Hi' */
复制代码
anotherPerson
以 person
为原型,具备 person
的全部属性和函数,并且在实例上定义了本身的 sayHi
函数。
仅适用于不考虑自定义类型和构造函数的状况下可使用这种模式。但因为不能作到函数复用,因此还不是最完美的方式。
前面提到的组合继承已是相对比较好的继承方式了。但它调用了两次 SuperType
构造函数,使得 SubType
原型和实例中拥有 name
和 colors
属性的两份拷贝。
咱们改进的思路是:
没必要为了指定 SubType 的原型而调用 SuperType 的构造函数,咱们须要的无非就是 SuperType 原型的一个副本而已。
咱们使用寄生式继承来继承 SuperType
的原型。
function inheritPrototype (subType, superType) {
var prototype = object(superType.prototype); /* 建立对象 */
prototype.constructor = subType; /* 加强对象 */
subType.prototype = prototype; /* 指定对象 */
}
function SuperType (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function () {
console.log(this.name);
}
function SubType (name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function () {
console.log(this.age);
}
复制代码
使用寄生组合式继承,咱们既实现了原型链,又避免了组合继承的两个调用构造函数的问题。