许多OOP语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。由于函数没有签名,在ECMAScript中无法实现接口继承。ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的。

实现对象集成集中方法如下:

一. 原型链继承

使用原型链集成的基本思想,是利用原型让一个引用类型集成另一个引用类型的属性和方法。

本质就是重写原型对象,代之以一个新类型的实例。

/* ================ 父类的定义 ================ */
function SuperType() {
    this.property = true;
}

SuperType.prototype.getSuperValue = function() {
    return this.property;
}

/* ================ 子类继承父类 ================ */
function SubType() {
    this.subProperty = false;
}

// 继承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
    return this.subProperty;
}

/* ================ 客户端实现 ================ */
var instance = new SubType();
alert("Sub: " + instance.getSubValue());
alert("Super: " + instance.getSuperValue());

原型链虽然很强大,但是用它来实现继承,最主要的问题来自包含引用类型值的原型。引用类型值的原型属性会被所有实例共享。

/* ================ 父类的定义 ================ */
function SuperType() {
    this.colors = ["red", "blue", "green"];
}

/* ================ 子类继承父类 ================ */
function SubType() {}

SubType.prototype = new SuperType();

/* ================ 客户端实现 ================ */
var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // "red", "blue", "green", "black"

var instance2 = new SubType();
console.log(instance2.colors); // "red", "blue", "green", "black"

所以,在实践中,很少会单独使用原型链。

二. 借用构造函数

借用构造函数技术也叫做伪造对象,或经典继承。基本思路是在子类型构造函数的内部调用超类型构造函数。

函数只是在特定环境中执行代码的对象,因此通过使用apply()和call()方法,也可以在新创建的对象上执行构造函数。

/* ================ 父类的定义 ================ */
function SuperType() {
    this.colors = ["red", "blue", "green"];
}

/* ================ 子类继承父类 ================ */
function SubType() {
    SuperType.call(this);
}

/* ================ 客户端实现 ================ */
var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // "red", "blue", "green", "black"

var instance2 = new SubType();
console.log(instance2.colors); // "red", "blue", "green"

使用call()方法(或apply()方法),实际上是在新创建的SubType实例的环境下调用了SuperType构造函数。

相对于原型链而言,借用构造函数有一个很大的优势,就是可以在子类型构造函数中向超类型构造函数传递参数。例如:

/* ================ 父类的定义 ================ */
function SuperType(name) {
    this.name = name;
}

/* ================ 子类继承父类 ================ */
function SubType() {
    SuperType.call(this, "Hello world");
    this.age = 12;
}

/* ================ 客户端实现 ================ */
var instance = new SubType();
alert(instance.name); // "Hello world"
alert(instance.age); // 12

由于在超类型中定义的方法,对于子类型而言是不可见的,结果所有类型都只能使用构造函数模式。因此借用构造函数的技术也很少单独使用。

三. 组合继承

组合继承又称伪经典继承,指的是将原型链和借用构造函数的技术结合到一块,从而发挥二者之长的一种继承模式。

其背后的思路是使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。

这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。

代码实现如下:

/* ================ 父类的定义 ================ */
function SuperType(name) {
    this.name = name;
}

/* ================ 子类继承父类 ================ */
function SubType() {
    SuperType.call(this, "Hello world");  // 借用构造函数
    this.age = 12;
}
SubType.prototype = new SuperType(); // 原型链继承

/* ================ 客户端实现 ================ */
var instance = new SubType();
alert(instance.name); // "Hello world"
alert(instance.age); // 12

四. 寄生式继承

寄生式继承与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后返回该对象。

代码实现如下:

// 基于已有对象借助原型创建新对象
function cloneObject(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

/* ================ 子类继承父类 ================ */
function createType(obj) {
    var clone = cloneObject(obj); // 创建新对象

    // 增强该对象的方法 ... ...
    clone.say = function() {
        console.log(this.name + " is " + this.age + " years old.");
    };

    return clone;
}

/* ================ 客户端实现 ================ */
var person = {
    name: "Bob",
    age: 21
};
var anotherPerson = createType(person);
anotherPerson.say(); // Bob is 21 years old.

五. 使用 class 关键字实现继承

"use strict";

/* ================ 父类的定义 ================ */
class SuperType {
  constructor(name) {
    this.name = name;
  }
}

/* ================ 子类继承父类 ================ */
class SubType extends SuperType {
  constructor(name) {
    super(name);
  }

  get say() {
    console.log("Hello " + this.name + ".");
  }
}

/* ================ 客户端实现 ================ */
var instance = new SubType("Bob");
instance.say(); // Hello Bob.
本文作者:子匠_Zijor,转载请注明出处:http://www.dengzhr.com/js/1032