ES之Class 继承

128 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情

extends

Class 可以通过 extends 关键字实现继承,让子类继承父类的属性和方法。extends 的写法比 ES5 的原型链继承,要清晰和方便很多。

class Parent {
  constructor(name) {
    this.name = name;
  }
}

class Children extends Parent {
  constructor(name, sex) {
    super(name);
    this.sex = sex;
  }
}

const c = new Children('baicai', 'man');
console.log('c =>', c); // c => Children { name: 'baicai', sex: 'man' }

子类 constructor 方法中必须有 super ,且必须出现在 this 之前。否则就会报错。

class Parent1 {
  /* ... */
}

// 当 constructor 存在是没有 super
class Children1 extends Parent1 {
  constructor() {}
}

// this 出现在 super 之前
class Children1 extends Parent1 {
  constructor(name) {
    this.name = name;
    super();
  }
}

let C1 = new Children1();
// Must call super constructor in derived class before accessing 'this' or returning from derived constructor at new Children1

报错的意思是说: 在访问 'this' 或从派生构造函数返回 new Children1 之前,必须在派生类中调用超级构造函数。

super 关键字用于访问和调用一个对象的父对象上的函数。

由此可以看出,子类的 this 对象,必须在父类的构造函数完成之后,获得与父类同样的实例属性和方法(父类的 this 对象),然后再对其进行加工,再添加子类自己的实例属性和方法。如果不调用 super() 方法,子类就得不到自己的 this 对象。

看个例子:

class Foo {
  constructor() {
    console.log(1);
  }
}

class Bar extends Foo {
  constructor() {
    super();
    console.log(2);
  }
}

const bar = new Bar();
// 1
// 2

上面的例子,会依次输出 1 和 2。

原因就是子类构造函数调用 super()时,会执行一次父类构造函数。

为什么呢?我们先看下一开始的那个 Parent 类, 如果用 ES5 怎么写。

function Parent(name) {
  this.name = name;
}

function Children(name, sex) {
  Parent.call(this, name);
  this.sex = sex;
}

// 让他们的原型保持一致
Children.prototype = Object.create(Parent.prototype);

var c = new Children('baicai', 'man');
console.log('c :>> ', c); // Children { name: 'baicai', sex: 'man' }

Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的 proto

ES5 先创造子类实例对象 Children ,然后再将父类的方法添加到这个对象上面.即'实例在前,继承在后';

ES6 则是先把父类的属性和方法加到一个空对象上,再将该对象作为子类的实例,即'继承在前,实例在后'

如何判断子类是否继承了另一类呢?可以使用 Object.getPrototypeOf() 方法

Object.getPrototypeOf(Children) === Parent;

不能继承常规对象。

var Father = {
    // ...
}
class Children extends Father {
     // ...
}
// Uncaught TypeError: Class extends value #<Object> is not a constructor or null

// 解决方案
Object.setPrototypeOf(Child.prototype, Father);

Object.setPrototypeOf(obj, prototype) 方法设置一个指定的对象的原型 ( 即,内部 [[Prototype]] 属性)到另一个对象或 null。

静态方法的继承

class Parent {
  static say() {
    return 'hellow';
  }
}

class Children extends Parent {
  constructor() {
    super();
  }

  static say2() {
    return super.say() + ' baicai';
  }
}

console.log('Children.say() :>> ', Children.say()); // Children.say() :>>  'hellow'
console.log('Children.say2() :>> ', Children.say2()); // Children.say2() :>>  hellow baicai