此页面由社区从英文翻译而来。了解更多并加入 MDN Web Docs 社区。

View in English Always switch to English

extends

基线
广泛可用

自 2016年3月 起,此特性已在主流浏览器中得到支持,可在大多数设备和浏览器版本中正常使用。

extends 关键字用于类声明或者类表达式中,以创建一个类,该类是另一个类的子类。

尝试一下

class DateFormatter extends Date {
  getFormattedDate() {
    const months = [
      "Jan",
      "Feb",
      "Mar",
      "Apr",
      "May",
      "Jun",
      "Jul",
      "Aug",
      "Sep",
      "Oct",
      "Nov",
      "Dec",
    ];
    return `${this.getDate()}-${months[this.getMonth()]}-${this.getFullYear()}`;
  }
}

console.log(new DateFormatter("August 19, 1975 23:15:30").getFormattedDate());
// Expected output: "19-Aug-1975"

语法

js
class ChildClass extends ParentClass { /* … */ }
ParentClass

求值为构造函数(包括类)或 null 的表达式。

描述

extends 关键字用来创建自定义类或者内置对象的子类。

任何可以用 new 调用并具有 prototype 属性的构造函数都可以作为候选的父类的构造函数。这两个条件必须同时成立——例如,绑定函数Proxy 可以被构造,但它们没有 prototype 属性,因此不能被子类化。

js
function OldStyleClass() {
  this.someProperty = 1;
}
OldStyleClass.prototype.someMethod = function () {};

class ChildClass extends OldStyleClass {}

class ModernClass {
  someProperty = 1;
  someMethod() {}
}

class AnotherChildClass extends ModernClass {}

ParentClassprototype 属性必须是 Objectnull,但在实践中很少需要担心这个问题,因为非对象的 prototype 无论如何都不会按照应有的方式运行(new 运算符会忽略它)。

js
function ParentClass() {}
ParentClass.prototype = 3;

class ChildClass extends ParentClass {}
// Uncaught TypeError: Class extends value does not have valid prototype property 3

console.log(Object.getPrototypeOf(new ParentClass()));
// [Object: null prototype] {}
// 实际上并不是一个数字!

extendsChildClassChildClass.prototype 设置了原型。

ChildClass 的原型对象 ChildClass.prototype 的原型对象
缺少 extends Function.prototype Object.prototype
extends null Function.prototype null
extends ParentClass ParentClass ParentClass.prototype
js
class ParentClass {}
class ChildClass extends ParentClass {}

// 允许静态属性的继承
Object.getPrototypeOf(ChildClass) === ParentClass;
// 允许实例属性的继承
Object.getPrototypeOf(ChildClass.prototype) === ParentClass.prototype;

extend 的右侧不一定是标识符。你可以使用任何求值为构造函数的表达式。这通常有助于创建混入(mixin)extends 表达式中的 this 值是围绕类定义的 this ,而引用类的名称会导致 ReferenceError,因为类尚未初始化。在此表达式中,awaityield 按预期工作。

js
class SomeClass extends class {
  constructor() {
    console.log("基类");
  }
} {
  constructor() {
    super();
    console.log("派生类");
  }
}

new SomeClass();
// 基类
// 派生类

基类可以从构造函数中返回任何内容,而派生类必须返回对象或 undefined ,否则将抛出 TypeError

js
class ParentClass {
  constructor() {
    return 1;
  }
}

console.log(new ParentClass()); // ParentClass {}
// 返回值将被忽略,因为它不是一个对象
// 这与函数构造函数一致

class ChildClass extends ParentClass {
  constructor() {
    super();
    return 1;
  }
}

console.log(new ChildClass()); // TypeError: Derived constructors may only return object or undefined

如果父类构造函数返回一个对象,则在进一步初始化类字段时,该对象将被用作派生类的 this 值。这种技巧被称为“返回覆盖”,它允许在无关对象上定义派生类的字段(包括私有字段)。

子类化内置类

警告:标准委员会目前的立场是,以前版本规范中的内置类的子类化机制设计过度,对性能和安全性造成了不可忽视的影响。新的内置方法较少考虑子类,引擎实现者正在研究是否要删除某些子类机制。在增强内置类时,请考虑使用组合而非继承。

下面是扩展类时可能会遇到的一些问题:

  • 在子类上调用静态工厂方法(如 Promise.resolve()Array.from())时,返回的实例始终是子类的实例。
  • 在子类上调用返回新实例的实例方法(如 Promise.prototype.then()Array.prototype.map())时,返回的实例始终是子类的实例。
  • 在可能的情况下,实例方法会尽量委托给最小的原始方法集。例如,对于 Promise 的子类,覆盖 then() 会自动导致 catch() 的行为发生变化;或对于