Skip to content
imshengli blog
Go back

ES6 Class 继承:extends、super 与原型链对比

ES6 类继承详解:extends、super、方法重写、静态方法继承,以及与 ES5 原型链继承的对比。

· 5 min

简介

Class 可以通过extends关键字实现继承。

基本用法

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  toString() {
    return `(${this.x}, ${this.y})`;
  }
  static hello() {
    return 'hello';
  }
}

// 通过 extends 实现继承
class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 必须调用 super,否则报错
    this.color = color; // super 之后才能用 this
  }
  // 方法重写
  toString() {
    return this.color + ' ' + super.toString();
  }
}

let cp = new ColorPoint(25, 8, 'green');
cp.toString(); // "green (25, 8)"
cp instanceof ColorPoint; // true
cp instanceof Point; // true

// 判断继承关系
Object.getPrototypeOf(ColorPoint) === Point; // true

super 关键字

super 既可以当函数使用,也可以当对象使用。

class Parent {
  constructor() {
    console.log(new.target.name);
  }
  sayHello() {
    return 'Hello from Parent';
  }
  static create() {
    return new this();
  }
}

class Child extends Parent {
  constructor() {
    // 作为函数:代表父类构造函数,内部 this 指向子类
    // 相当于 Parent.prototype.constructor.call(this)
    super(); // 输出 "Child"
  }
  sayHello() {
    // 作为对象:在普通方法中指向父类原型对象
    return super.sayHello() + ' and Child';
  }
  static create() {
    // 在静态方法中 super 指向父类本身
    return super.create();
  }
}

通过 super 调用父类方法时,方法内部 this 指向子类实例:

class A {
  constructor() { this.x = 1; }
  print() { console.log(this.x); }
}

class B extends A {
  constructor() { super(); this.x = 2; }
  m() { super.print(); } // 调用 A.prototype.print,但 this 指向 B 实例
}

new B().m(); // 2

方法重写

class Animal {
  speak() { return 'Some sound'; }
  move(distance = 0) { return `moved ${distance}m`; }
}

class Dog extends Animal {
  speak() { return 'Woof!'; } // 完全重写
  move(distance = 5) {
    console.log('Running...');
    return super.move(distance); // 扩展父类方法
  }
}

静态方法继承

class Base {
  static greet() { return 'Hello from Base'; }
  static create(...args) { return new this(...args); }
}

class Derived extends Base {
  constructor(name) { super(); this.name = name; }
}

Derived.greet(); // "Hello from Base"(静态方法自动继承)
Derived.create('test').name; // "test"(this 指向调用者)

__proto__ 和 prototype

class A {}
class B extends A {}

// 子类 __proto__ 指向父类(构造函数的继承)
B.__proto__ === A; // true
// 子类 prototype.__proto__ 指向父类 prototype(方法的继承)
B.prototype.__proto__ === A.prototype; // true

// 内部实现等价于
Object.setPrototypeOf(B.prototype, A.prototype);
Object.setPrototypeOf(B, A);

与 ES5 原型链继承的对比

// ES5 写法
function Animal(name) { this.name = name; }
Animal.prototype.speak = function() { return this.name + ' speaks'; };

function Dog(name, breed) {
  Animal.call(this, name); // 借用构造函数
  this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // 修复 constructor

// ES6 写法
class Animal2 {
  constructor(name) { this.name = name; }
  speak() { return this.name + ' speaks'; }
}
class Dog2 extends Animal2 {
  constructor(name, breed) { super(name); this.breed = breed; }
}
对比项ES5ES6
语法手动设置原型链extends 关键字
super 调用Parent.call(this)super()
静态方法继承需手动复制自动继承
原生构造函数继承无法正确继承可以继承
执行顺序先创建子类 this,再调父类先创建父类 this,再交给子类

原生构造函数的继承

ES5 无法继承原生构造函数,因为其 this 无法绑定。ES6 先新建父类实例 this,再用子类构造函数修饰,因此可以正确继承。

class MyArray extends Array {
  constructor(...args) { super(...args); }
  first() { return this[0]; }
  last() { return this[this.length - 1]; }
}

var arr = new MyArray(1, 2, 3);
arr.first(); // 1
arr.last(); // 3
arr.length = 2;
arr; // MyArray [1, 2]
// 继承 Error 实现自定义错误
class HttpError extends Error {
  constructor(statusCode, message) {
    super(message);
    this.name = 'HttpError';
    this.statusCode = statusCode;
  }
}

try {
  throw new HttpError(404, 'Not Found');
} catch (e) {
  e.statusCode; // 404
  e.message; // "Not Found"
  e.stack; // 正确的调用栈
}

实际应用示例

class Component {
  constructor(props = {}) {
    this.props = props;
    this.state = {};
  }
  setState(newState) {
    this.state = { ...this.state, ...newState };
    this.render();
  }
  render() {
    throw new Error('子类必须实现 render 方法');
  }
}

class Button extends Component {
  constructor(props) {
    super(props);
    this.state = { clicked: false };
  }
  handleClick() { this.setState({ clicked: true }); }
  render() { return `<button>${this.props.text}</button>`; }
}

const btn = new Button({ text: 'Submit' });
btn.render(); // "<button>Submit</button>"

参考文档


Share this post on:

Previous Post
ES6 Class:语法糖背后的构造函数与原型
Next Post
前端性能优化:网络、渲染、代码与图片全链路指南