简介
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; }
}
| 对比项 | ES5 | ES6 |
|---|---|---|
| 语法 | 手动设置原型链 | 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>"