Javascript中的继承

61 阅读3分钟

原型链继承

function SuperType(){
    this.friends = ['darthvader','obiwan']
}
function SubType(){
}
SubType.prototype = new SuperType()
SuperType.prototype.getFriends = function(){
    return this.friends
}
let instance1 = new SubType()
instance1.friends.push('anakin')
console.log(instance1.friends) // ['darthvader', 'obiwan', 'anakin']
let instance2 = new SubType()
console.log(instance2.friends) // ['darthvader', 'obiwan', 'anakin']
console.log(instance1.getFriends()) // ['darthvader', 'obiwan', 'anakin']

缺点: 当原型中包含引用值的时候,原型中包含的引用值会在所有实例间共享,这就是为什么属性通常会定义在构造函数中而不会定义在原型上面的原因。

借用构造函数

基本思路就是在子类构造函数中调用父类的构造函数

function SuperType(){
    this.colors = ['red','yellow','blue']
}
function SubType(){
     SuperType.call(this)
   }
let instanc1 = new SubType()
instance1.colors.push('black')
console.log(instance1.colors) // ['red','yellow','blue','black']
let instance2 = new SubType()
console.log(instance2.colors) // ['red','yellow','blue']

盗用构造函数的主要缺点: 必须在构造函数中定义方法,因此函数不能重用,子类不能访问父类原型上定义的方法。

组合继承

function SuperType(name){
    this.name = name
    this.colors = ["red","blue","green"]
}
SubType.prototype = new SuperType()
function SubType(name,age){
    SuperType.call(this,name)
    this.age = age
}
SuperType.prototype.sayName = function(){
    console.log(this.name)
}
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

缺点: 调用了两次构造函数

原型式继承

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)
yetAnotherPerson.name = "Linda"
yetAnotherPerson.friends.push("Barbie")
console.log(person.friends) // ['Shelby', 'Court', 'Van', 'Rob', 'Barbie']

这个 object()函数会创建一个临时构造函数,将传入的对象赋值给这个构造函数的原型,然后返回这个临时类型的一个实例。本质上,object()是对传入的对象执行了一次浅复制。

ECMAScript 5 通过增加 Object.create()方法将原型式继承的概念规范化了。这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在只有一个参数时,Object.create()与这里的 object()方法效果相同

let person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};

let anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

let yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"

原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合。但要记住,属性中包含的引用值始终会在相关对象间共享,跟使用原型模式是一样的。

寄生继承

寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。(创建一个浅复制,然后增强这个浅复制)

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"

寄生组合继承

组合继承存在效率问题:父类构造函数始终被调用两次。 寄生组合式继承通过盗用构造函数继承属性,但是用混合式原型链继承方法。 基本思路是:不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本。说到底就是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型 寄生组合继承的基本模式如下:

function inheritPrototype(subType, superType) {
    let prototype = object(superType.prototype); // 创建对象
    prototype.constructor = subType; // 增强对象
    subType.prototype = prototype; // 赋值对象
}
// 另一个版本
function inheritPrototype(subType, superType) {
    let subPrototype = Object.create(superType.prototype);
    subPrototype.constructor = subType;
    subType.prototype = subPrototype;
}
// 另一个版本
function inheritPrototype(subType, superType) {
    let subType.prototype = Object.create(superType.prototype)
    subType.prototype.constructor = subType
}
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);
};