ES6-class-extends

继承 inherit

class 是对原型继承的一种语法糖的包装。那相对于原型继承,它有什么优点呢?
我们来先看一个典型的基于原型链继承的例子。部分内容来自“Javascript高级程序设计”

function SuperType() {
    this.property = true;
}

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

function SubType() {
    this.subProperty = false;
}

SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function() {
    return this.subProperty;
}

var instance = new SubType();
console.log(instance.getSuperValue());  // true
console.log(instance instanceof Object);  // true
console.log(instance instanceof SuperType);  // true
console.log(instance instanceof SubType);  // true

问题,当包含引用类型的值。

function SuperType() {
    this.colors = ["red", "blue", "green"];
}

function SubType() {
   
}

SubType.prototype = new SuperType();


var instance = new SubType();
instance.colors.push("black");
var instance1 = new SubType();
instance1.colors.push("white");
console.log(instance.colors);  // [ 'red', 'blue', 'green', 'black', 'white' ]
console.log(instance1.colors);  // [ 'red', 'blue', 'green', 'black', 'white' ]

解决方案:

  • 借用构造函数
function SuperType() {
    this.colors = ["red", "blue", "green"];
}

function SubType() {
   SuperType.call(this);
}

SubType.prototype = new SuperType();


var instance = new SubType();
instance.colors.push("black");
var instance1 = new SubType();
instance1.colors.push("white");
console.log(instance.colors);
console.log(instance1.colors);
  • 组合继承
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);
}
  • 寄生组合式继承
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

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);
}
var instance = new SubType("Tom", 70);
instance.colors.push("black");
var instance1 = new SubType("Jerry", 69);
instance1.colors.push("white");
console.log(instance.colors);
console.log(instance.sayName());
console.log(instance.sayAge());
console.log(instance1.colors);
console.log(instance1.sayName());
console.log(instance1.sayAge());

MDN 原型链继承
(欠图一张)

extends

从es5来说,实现对象的继承,还是相当麻烦的。而extends 关键字的出现,使继承变得简单,原型会自动进行调整,super()/super关键字可以访问父类的构造方法和属性。

class Animal { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Dog extends Animal {
  speak() {
    console.log(this.name + ' barks.');
  }
}

var d = new Dog('Mitzie');
d.speak();// 'Mitzie barks.'

分析:Dog类没有构造函数,这样合理吗?

// 等价于上个类定义
class Dog extends Animal {
  constructor(name) {
    super(name)
  }
  speak() {
    console.log(this.name + ' barks.');
  }
}

super()方法调用注意:

  • 只可在以extends 实现的派生类中的constructor方法中调用,在非派生类或方法中直接调用,会报错。
  • 在constructor中访问this之前,一定要先调用super(),因为它负责初始化this,如果在super()调用之前尝试访问this,会报错。
  • 如果不想调用super(),则唯一的方法是让类的constructor()返回一个对象。

类方法遮蔽

说明:派生类中的方法总会覆盖基类中的同名方法。

class Animal { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Dog extends Animal {
  speak() {
    console.log(this.name + ' barks.');
  }
}
// 基类中的speak()方法被覆盖

静态类成员继承

说明:如果基类有静态成员,那么这些静态成员在派生类中也可用。

class Animal { 
    constructor(name) {
      this.name = name;
    }
    
    speak() {
      console.log(this.name + ' makes a noise.');
    }
    static create(name) {
        return new Animal(name);
    }
  }
  
  class Dog extends Animal {
    speak() {
      console.log(this.name + ' barks.');
    }
  }

  let a1 = Animal.create("Monkey");
  let a2 = Dog.create("BeijinDog");
  console.log(a1 instanceof Animal);  // true
  console.log(a2 instanceof Animal);  // true
  console.log(a2 instanceof Dog);  // false 这个是不是很意外?

派生自表达式的类

内建对象继承

Symbol.species

constructor中new.target

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理