Movatterモバイル変換


[0]ホーム

URL:


MDN Web Docs

extends

BaselineWidely available

Theextends keyword is used inclass declarations orclass expressions to create a class that is a child of another class.

Try it

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"

Syntax

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

An expression that evaluates to a constructor function (including a class) ornull.

Description

Theextends keyword can be used to subclass custom classes as well as built-in objects.

Any constructor that can be called withnew and has theprototype property can be the candidate for the parent class. The two conditions must both hold — for example,bound functions andProxy can be constructed, but they don't have aprototype property, so they cannot be subclassed.

js
function OldStyleClass() {  this.someProperty = 1;}OldStyleClass.prototype.someMethod = function () {};class ChildClass extends OldStyleClass {}class ModernClass {  someProperty = 1;  someMethod() {}}class AnotherChildClass extends ModernClass {}

Theprototype property of theParentClass must be anObject ornull, but you would rarely worry about this in practice, because a non-objectprototype doesn't behave as it should anyway. (It's ignored by thenew operator.)

js
function ParentClass() {}ParentClass.prototype = 3;class ChildClass extends ParentClass {}// Uncaught TypeError: Class extends value does not have valid prototype property 3console.log(Object.getPrototypeOf(new ParentClass()));// [Object: null prototype] {}// Not actually a number!

extends sets the prototype for bothChildClass andChildClass.prototype.

Prototype ofChildClassPrototype ofChildClass.prototype
extends clause absentFunction.prototypeObject.prototype
extends nullFunction.prototypenull
extends ParentClassParentClassParentClass.prototype
js
class ParentClass {}class ChildClass extends ParentClass {}// Allows inheritance of static propertiesObject.getPrototypeOf(ChildClass) === ParentClass;// Allows inheritance of instance propertiesObject.getPrototypeOf(ChildClass.prototype) === ParentClass.prototype;

The right-hand side ofextends does not have to be an identifier. You can use any expression that evaluates to a constructor. This is often useful to createmixins. Thethis value in theextends expression is thethis surrounding the class definition, and referring to the class's name is aReferenceError because the class is not initialized yet.await andyield work as expected in this expression.

js
class SomeClass extends class {  constructor() {    console.log("Base class");  }} {  constructor() {    super();    console.log("Derived class");  }}new SomeClass();// Base class// Derived class

While the base class may return anything from its constructor, the derived class must return an object orundefined, or aTypeError will be thrown.

js
class ParentClass {  constructor() {    return 1;  }}console.log(new ParentClass()); // ParentClass {}// The return value is ignored because it's not an object// This is consistent with function constructorsclass ChildClass extends ParentClass {  constructor() {    super();    return 1;  }}console.log(new ChildClass()); // TypeError: Derived constructors may only return object or undefined

If the parent class constructor returns an object, that object will be used as thethis value for the derived class when further initializingclass fields. This trick is called"return overriding", which allows a derived class's fields (includingprivate ones) to be defined on unrelated objects.

Subclassing built-ins

Warning:The standard committee now holds the position that the built-in subclassing mechanism in previous spec versions is over-engineered and causes non-negligible performance and security impacts. New built-in methods consider less about subclasses, and engine implementers areinvestigating whether to remove certain subclassing mechanisms. Consider using composition instead of inheritance when enhancing built-ins.

Here are some things you may expect when extending a class:

  • When calling a static factory method (likePromise.resolve() orArray.from()) on a subclass, the returned instance is always an instance of the subclass.
  • When calling an instance method that returns a new instance (likePromise.prototype.then() orArray.prototype.map()) on a subclass, the returned instance is always an instance of the subclass.
  • Instance methods try to delegate to a minimal set of primitive methods where possible. For example, for a subclass ofPromise, overridingthen() automatically causes the behavior ofcatch() to change; or for a subclass ofMap, overridingset() automatically causes the behavior of theMap() constructor to change.

However, the above expectations take non-trivial efforts to implement properly.

  • The first one requires the static method to read the value ofthis to get the constructor for constructing the returned instance. This means[p1, p2, p3].map(Promise.resolve) throws an error because thethis insidePromise.resolve isundefined. A way to fix this is to fall back to the base class ifthis is not a constructor, likeArray.from() does, but that still means the base class is special-cased.
  • The second one requires the instance method to readthis.constructor to get the constructor function. However,new this.constructor() may break legacy code, because theconstructor property is both writable and configurable and is not protected in any way. Therefore, many copying built-in methods use the constructor's[Symbol.species] property instead (which by default just returnsthis, the constructor itself). However,[Symbol.species] allows running arbitrary code and creating instances of arbitrary type, which poses a security concern and greatly complicates subclassing semantics.
  • The third one leads to visible invocations of custom code, which makes a lot of optimizations harder to implement. For example, if theMap() constructor is called with an iterable ofx elements, then it must visibly invoke theset() methodx times, instead of just copying the elements into the internal storage.

These problems are not unique to built-in classes. For your own classes, you will likely have to make the same decisions. However, for built-in classes, optimizability and security are a much bigger concern. New built-in methods always construct the base class and call as few custom methods as possible. If you want to subclass built-ins while achieving the above expectations, you need to override all methods that have the default behavior baked into them. Any addition of new methods on the base class may also break the semantics of your subclass because they are inherited by default. Therefore, a better way to extend built-ins is to usecomposition.

Extending null

extends null was designed to allow easy creation ofobjects that do not inherit fromObject.prototype. However, due to unsettled decisions about whethersuper() should be called within the constructor, it's not possible to construct such a class in practice using any constructor implementation that doesn't return an object.The TC39 committee is working on re-enabling this feature.

js
new (class extends null {})();// TypeError: Super constructor null of anonymous class is not a constructornew (class extends null {  constructor() {}})();// ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructornew (class extends null {  constructor() {    super();  }})();// TypeError: Super constructor null of anonymous class is not a constructor

Instead, you need to explicitly return an instance from the constructor.

js
class NullClass extends null {  constructor() {    // Using new.target allows derived classes to    // have the correct prototype chain    return Object.create(new.target.prototype);  }}const proto = Object.getPrototypeOf;console.log(proto(proto(new NullClass()))); // null

Examples

Using extends

The first example creates a class calledSquare from a class calledPolygon. This example is extracted from thislive demo(source).

js
class Square extends Polygon {  constructor(length) {    // Here, it calls the parent class' constructor with lengths    // provided for the Polygon's width and height    super(length, length);    // Note: In derived classes, super() must be called before you    // can use 'this'. Leaving this out will cause a reference error.    this.name = "Square";  }  get area() {    return this.height * this.width;  }}

Extending plain objects

Classes cannot extend regular (non-constructible) objects. If you want to inherit from a regular object by making all properties of this object available on inherited instances, you can instead useObject.setPrototypeOf():

js
const Animal = {  speak() {    console.log(`${this.name} makes a noise.`);  },};class Dog {  constructor(name) {    this.name = name;  }}Object.setPrototypeOf(Dog.prototype, Animal);const d = new Dog("Mitzie");d.speak(); // Mitzie makes a noise.

Extending built-in objects

This example extends the built-inDate object. This example is extracted from thislive demo(source).

js
class MyDate 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()}`;  }}

ExtendingObject

All JavaScript objects inherit fromObject.prototype by default, so writingextends Object at first glance seems redundant. The only difference from not writingextends at all is that the constructor itself inherits static methods fromObject, such asObject.keys(). However, because noObject static method uses thethis value, there's still no value in inheriting these static methods.

TheObject() constructor special-cases the subclassing scenario. If it's implicitly called viasuper(), it always initializes a new object withnew.target.prototype as its prototype. Any value passed tosuper() is ignored.

js
class C extends Object {  constructor(v) {    super(v);  }}console.log(new C(1) instanceof Number); // falseconsole.log(C.keys({ a: 1, b: 2 })); // [ 'a', 'b' ]

Compare this behavior with a custom wrapper that does not special-case subclassing:

js
function MyObject(v) {  return new Object(v);}class D extends MyObject {  constructor(v) {    super(v);  }}console.log(new D(1) instanceof Number); // true

Species

You might want to returnArray objects in your derived array classMyArray. The species pattern lets you override default constructors.

For example, when using methods such asArray.prototype.map() that return the default constructor, you want these methods to return a parentArray object, instead of theMyArray object. TheSymbol.species symbol lets you do this:

js
class MyArray extends Array {  // Overwrite species to the parent Array constructor  static get [Symbol.species]() {    return Array;  }}const a = new MyArray(1, 2, 3);const mapped = a.map((x) => x * x);console.log(mapped instanceof MyArray); // falseconsole.log(mapped instanceof Array); // true

This behavior is implemented by many built-in copying methods. For caveats of this feature, see thesubclassing built-ins discussion.

Mix-ins

Abstract subclasses ormix-ins are templates for classes. A class can only have a single superclass, so multiple inheritance from tooling classes, for example, is not possible. The functionality must be provided by the superclass.

A function with a superclass as input and a subclass extending that superclass as output can be used to implement mix-ins:

js
const calculatorMixin = (Base) =>  class extends Base {    calc() {}  };const randomizerMixin = (Base) =>  class extends Base {    randomize() {}  };

A class that uses these mix-ins can then be written like this:

js
class Foo {}class Bar extends calculatorMixin(randomizerMixin(Foo)) {}

Avoiding inheritance

Inheritance is a very strong coupling relationship in object-oriented programming. It means all behaviors of the base class are inherited by the subclass by default, which may not always be what you want. For example, consider the implementation of aReadOnlyMap:

js
class ReadOnlyMap extends Map {  set() {    throw new TypeError("A read-only map must be set at construction time.");  }}

It turns out thatReadOnlyMap is not constructible, because theMap() constructor calls the instance'sset() method.

js
const m = new ReadOnlyMap([["a", 1]]); // TypeError: A read-only map must be set at construction time.

We may get around this by using a private flag to indicate whether the instance is being constructed. However, a more significant problem with this design is that it breaks theLiskov substitution principle, which states that a subclass should be substitutable for its superclass. If a function expects aMap object, it should be able to use aReadOnlyMap object as well, which will break here.

Inheritance often leads tothe circle-ellipse problem, because neither type perfectly entails the behavior of the other, although they share a lot of common traits. In general, unless there's a very good reason to use inheritance, it's better to use composition instead. Composition means that a class has a reference to an object of another class, and only uses that object as an implementation detail.

js
class ReadOnlyMap {  #data;  constructor(values) {    this.#data = new Map(values);  }  get(key) {    return this.#data.get(key);  }  has(key) {    return this.#data.has(key);  }  get size() {    return this.#data.size;  }  *keys() {    yield* this.#data.keys();  }  *values() {    yield* this.#data.values();  }  *entries() {    yield* this.#data.entries();  }  *[Symbol.iterator]() {    yield* this.#data[Symbol.iterator]();  }}

In this case, theReadOnlyMap class is not a subclass ofMap, but it still implements most of the same methods. This means more code duplication, but it also means that theReadOnlyMap class is not strongly coupled to theMap class, and does not easily break if theMap class is changed, avoiding thesemantic issues of built-in subclassing. For example, if theMap class adds a new utility method (such asgetOrInsert()) that does not callset(), it would cause theReadOnlyMap class to no longer be read-only unless the latter is updated accordingly to overridegetOrInsert() as well. Moreover,ReadOnlyMap objects do not have theset method at all, which is more accurate than throwing an error at runtime.

Specifications

Specification
ECMAScript® 2026 Language Specification
# sec-class-definitions

Browser compatibility

See also

Help improve MDN

Learn how to contribute.

This page was last modified on byMDN contributors.


[8]ページ先頭

©2009-2025 Movatter.jp