javaScript的各种继承

134 阅读1分钟

构造函数式继承

function Super(age){
  this.nextYearAge = age;
}

Super.prototype.getNextYearAge = function(){
  return `You will be {this.lastYearAge} next year`;
}

function Sub(age){
  Super.call(this, age);
  this.nowAge = age;
}

Sub.prototype.getNowAge = function(){
  return `Now your age is ${this.nowAge}`;
}

const instance = new Sub(18);
console.log(instance.nowAge); // 18
console.log(instance.nextYearAge); // 19
console.log(instance.getNowAge()); // Now your age is 18
console.log(instance.getNextYearAge()); // TypeError: instance.getNextYearAge is not a function

可以看到,这种继承方式不可以继承父类构造函数的原型

通过原型链

function Super(){
  this.friends = ['xiaoming', 'xiaohua', 'xiaoli'];
}

Super.prototype.getFriends = function(){
  return this.friends.join(',');
}
function Sub(){};
Sub.prototype = new Super(); // Super的一个实例直接赋给Sub的原型
console.log(Sub.prototype.constructor); // [Function: Super]
const instance1 = new Sub();
const instance2 = new Sub();
console.log(instance1.friends);
console.log(instance2.friends);

instance1.friends.push('xiaoqiang');

console.log(instance1.getFriends());
console.log(instance2.getFriends());// instance1在实例引用类型上的修改也在instance2上反应出来

子类的实例也可以继承超类的属性和超类原型上的方法,但是子类原型的构造函数会变成超类。如果超类的属性值是引用类型,子类一个实例对该属性的修改也会在其它实例反应出来,这显然不是我们想要的结果

组合继承

function Super(...friends){
  this.friends = friends;
};

Super.prototype.getFriends = function(){
  return this.friends.join(',');
};

function Sub(friends, age){
  Super.apply(this, friends);
  this.age = age;
};

Sub.prototype = new Super();

Sub.prototype.getAge = function(){
  return this.age;
};

const instance = new Sub(['xiaoming', 'xiaoli'], 18);
console.log(instance.getFriends());
console.log(instance.getAge());

// Sub的原型和实例上都添加了friends属性
console.log(instance.hasOwnProperty('friends')); // true
console.log(Sub.prototype.hasOwnProperty('friends')); // true

// Sub原型的constructor也不再指向自身
console.log(Sub.prototype.constructor); // [Function: Super]

组合模式是应用得最多的模式,但也不是没问题的,超类的构造函数被运行了两次,一次是给子类的原型赋值(也给子类的原型添加了属性,这大多不是我们想要的结果),另一次是子类生成实例时盗用了超类的构造函数来添加属性。我们还需要手动调用Object.defineProperty()来给子类原型添加constructor属性(否则Sub.constructor会顺着原型链查找,这里就会找到Super.prototype.constructor属性,当然值就是Super构造函数了)

寄生式组合继承

其背后的基本思路是:不必为了指定子类的原型而去调用父类的构造函数,我们需要的仅是超类原型的一个副本。也就是说,我们可以借助寄生式继承(一个空的构造函数)来继承超类型的原型,然后再把结果指定给子类型的原型。

function middle(obj){ //借用一个空的构造函数来引用超类的原型
    const fn = new Function();
    fn.prototype = obj;
    return new fn();
};

function Super(...friends){
    this.friends = friends;
};

Super.prototype.getFriends = function(){
    return this.friends.join(',');
};
function Sub(friends, age){
    Super.call(this, ...friends);
    this.age = age;
};

Sub.prototype = middle(Super.prototype); // 这里只继承超类的原型,不需要执行超类的构造函数
Sub.prototype.getAge = function(){
    return `Your age is ${this.age}`;
};

const instance = new Sub(['chenshun', 'xiaoqiang'], 18);
console.log(instance.getFriends()); // chenshun,xiaoqiang
console.log(instance.getAge()); // Your age is 18
console.log(Sub.prototype.hasOwnProperty('friends')); // false
console.log(Sub.prototype.hasOwnProperty('constructor')); // false
console.log(Sub.prototype.constructor); // [Function: Super]

这种模式仍然要手动为子类指定constructor属性。

类式继承

类式继承即囊括了寄生组合式继承的优点,又不用为子类重新设置constructor属性,简单易用。

class Super{
    constructor(friends){
        this.friends = friends;
    }
    getFrinends(){
        return this.friends.join(',');
    }
};

class Sub extends Super{
    constructor(friends, age){
        super(friends);
        this.age = age;
    }
    getAge(){
        return this.age;
    }
}

const instance = new Sub(['chenshun', 'xiaoqiang'], 18);
console.log(instance.getFrinends());
console.log(instance.getAge());

// 不用手动指定constructor
console.log(Sub.prototype.hasOwnProperty('constructor')); // true
console.log(Sub.prototype.constructor); // [Function: Sub]