很多面向对象的语言都接受两种继承:接口继承和实现继承。
前者只继承方法签名,后者继承实际的方法。接口继承在 ECMAScript 中是不可能的,因为函数没有签
名。实现继承是 ECMAScript 唯一支持的继承方式,而这主要是通过原型链实现的。
1.原型链继承
通过原型链继承的方式,SubType可以继承SuperType构造函数上的所有属性(自定义的属性&&原型链上的属性)
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; }; let instance = new SubType(); console.log(instance);
问题:1.当原型链中包含引用类型的时候,会在所有实例间共享
2.原型链的第二个问题是,子类型在实例化时不能给父类型的构造函数传参。
2.盗用构造函数
apply()和 call()方法以新创建的对象为上下文执行构造函数
解决了原型链继承中出现引用类型不能在实例间共享的问题
function SuperType() { this.colors = ["red", "blue", "green"]; } function SubType() { // 继承 SuperType SuperType.call(this); } let instance1 = new SubType(); instance1.colors.push("black"); console.log(instance1.colors); // "red,blue,green,black" let instance2 = new SubType(); console.log(instance2.colors); // "red,blue,green"
并且使用该种方式,也可以解决原型链上不能传参的问题
function SuperType(name){ this.name = name; } function SubType() { // 继承 SuperType 并传参 SuperType.call(this, "Nicholas"); // 实例属性 this.age = 29; } let instance = new SubType(); console.log(instance.name); // "Nicholas";
问题: 1.必须在构造函数中声明方法,不能重用 2.子类不能访问父类上面的原型方法
3.组合继承
组合继承通过构造函数的方式继承实例属性,通过原型链继承的方式继承属性和方法,可以让属性在实例间私有,同时可以通过原型链继承的方式实现方法间的共享
组合继承弥补了原型链和盗用构造函数的不足,是 JavaScript 中使用最多的继承模式。而且组合继承也保留了 instanceof 操作符和 isPrototypeOf()方法识别合成对象的能力。
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; } // 继承方法 SubType.prototype = new SuperType(); SubType.prototype.sayAge = function() { console.log(this.age); }; let instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); console.log(instance1.colors); // "red,blue,green,black" instance1.sayName(); // "Nicholas"; instance1.sayAge(); // 29 let instance2 = new SubType("Greg", 27); console.log(instance2.colors); // "red,blue,green" instance2.sayName(); // "Greg"; instance2.sayAge(); // 27
4.原型式继承
原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合。属性中包含的引用值始终会在相关对象间共享,跟使用原型模式是一样的。
function object(o) { function F() {} F.prototype = o; return new F(); } let person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; let anotherPerson = object(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); let yetAnotherPerson = object(person); // let yetAnotherPerson = Object.create(person); // 这里的Object.create方法等同于object方法 yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"
5.寄生式继承
创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象
function createAnother(original){ let clone = object(original); // 通过调用函数创建一个新对象 clone.sayHi = function() { // 以某种方式增强这个对象 console.log("hi"); }; return clone; // 返回这个对象 } let person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; let anotherPerson = createAnother(person); anotherPerson.sayHi(); // "hi"
6.寄生式组合继承
function inheritPrototype(subType, superType) { let 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); };
7.extends
ES6 类支持单继承。使用 extends 关键字,就可以继承任何拥有[[Construct]]和原型的对象
在类构造函数中使用 super 可以调用父类构造函数。
如果在派生类中显式定义了构造函数,则要么必须在其中调用super(),要么必须在其中返回一个对象。
class Vehicle {} // 继承类 class Bus extends Vehicle {} let b = new Bus(); console.log(b instanceof Bus); // true console.log(b instanceof Vehicle); // true function Person() {} // 继承普通构造函数 class Engineer extends Person {} let e = new Engineer(); console.log(e instanceof Engineer); // true console.log(e instanceof Person); // true