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 {}
ParentClass 的 prototype 属性必须是 Object 或 null,但在实践中很少需要担心这个问题,因为非对象的 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] {}
// 实际上并不是一个数字!
extends 为 ChildClass 和 ChildClass.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,因为类尚未初始化。在此表达式中,await 和 yield 按预期工作。
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()的行为发生变化;或对于