new.target
Baseline
Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since 2017年9月.
new.target はメタプロパティで、関数��コンストラクターが new 演算子を使用して呼び出されたかどうかを検出することができます。 new 演算子を使用して呼び出したコンストラクターや 関数の中では、 new.target は new が呼び出されたコンストラクターまたは関数への参照を返します。通常に呼び出された関数の中では、 new.target は undefined になります。
試してみましょう
function Foo() {
if (!new.target) {
throw new TypeError("new を付けずに Foo コンストラクターを呼び出すのは不正です");
}
}
try {
Foo();
} catch (e) {
console.log(e);
// 予想される結果: TypeError: new を付けずに Foo コンストラクターを呼び出すのは不正です
}
構文
new.target
値
new.target は、構築可能な関数値または undefined であることが保証されています。
- クラスのコンストラクター内では、
newが呼び出されたクラスを参照します。これは現在のコンストラクターのサブクラスである可能性があります。サブクラスはsuper()を通じてスーパークラスのコンストラクターを経過的に呼び出すためです。 - 通常の関数では、関数が
newで直接構築された場合、new.targetはその関数自体を参照します。関数がnewなしで呼び出された場合、new.targetはundefinedになります。関数はextendsの基底クラスとして使用されることがあり、その場合new.targetはサブクラスを参照する可能性があります。 - コンストラクター(クラスまたは関数)が
Reflect.construct()経由で呼び出された場合、new.targetはnewTargetとして渡された値(既定はtarget)を参照します。 - アロー関数では、
new.targetは周囲のスコープから継承されます。アロー関数がnew.targetのバインディングを持つ別のクラスや関数内で定義されていない場合、構文エラーが発生します。 - 静的初期化ブロック内では、
new.targetはundefinedです。
解説
new.target 構文は、キーワード new とドットと target 識別子で構成されています。new は識別子ではなく予約語であるため、これはプロパティアクセサーではなく、特別な式構文です。
new.target メタプロパティは、すべての関数/クラスの本体内で利用できます。関数やクラス の外部で new.target を使用すると構文エラーになります。
例
関数呼び出しにおける new.target の使用
通常の関数呼び出しでは (コンストラクター関数の呼び出しとは��照的に)、 new.target は undefined になります。これにより、関数が new 付きでコンストラクターとして呼び出されたかを検出できます。
function Foo() {
if (!new.target) {
throw new Error("Foo() は new を付けて呼び出さなくてはなりません");
}
console.log("Foo が new 付きでインスタンス化されました");
}
new Foo(); // "Foo が new 付きでインスタンス化されました" を出力
Foo(); // "Foo() は new を付けて呼び出さなくてはなりません" 例外が発生
コンストラクターにおける new.target
クラスのコンストラクターでは、new.target は new で直接実行されたコンストラクターを参照します。これは、コンストラクターが親クラスにあり、子コンストラクターから委任された場合も同様です。new.target は、new が呼び出されたクラスを指します。例えば、b が new B() を使用して初期化された際には、B の名前が表示されます。同様に、a の場合、クラス A の名前が表示されます。
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
const a = new A(); // Logs "A"
const b = new B(); // Logs "B"
Reflect.construct() を使用したときの new.target
Reflect.construct() やクラスが登場する以前は、継承を実装する一般的な方法として、this の値を渡し、基底クラスのコンストラクターでそれを変更させる手法が用いられていました。
function Base() {
this.name = "Base";
}
function Extended() {
// Base() コンストラクターが、 `new` によって生成される新しいオブジェクト
// ではなく、既存の `this` 値に対して動作する唯一の 方法。
Base.call(this);
this.otherProperty = "Extended";
}
Object.setPrototypeOf(Extended.prototype, Base.prototype);
Object.setPrototypeOf(Extended, Base);
console.log(new Extended()); // Extended { name: 'Base', otherProperty: 'Extended' }
ただし、call() および apply() は実際には関数を「構築」するのではなく「呼び出し」するため、new.target の値は undefined になります。これは、Base() が new で構築されたかどうかを確認する場合、エラーが発生するか、それ以外にも予期しない動作を引き起こす可能性があるということの意味します。例えば、Map() コンストラクターは new なしでは呼び出せないため、この方法で Map を拡張することはできません。
すべての組み込みコンストラクターは、 new.target.prototype を読み取ることで、新規インスタンスのプロトタイプチェーン全体を直接構築します。したがって、(1) Base が new で構築され、(2) new.target が Base 自体ではなくサブクラスを指すようにするには、 Reflect.construct() を使用する必要があります。
function BetterMap(entries) {
// 基底クラスのコンストラクターを呼び出すが、`new.target` をサブクラスに設定する。
// これにより、作成されるインスタンスに正しいプロトタイプチェーンが構築される。
return Reflect.construct(Map, [entries], BetterMap);
}
BetterMap.prototype.upsert = function (key, actions) {
if (this.has(key)) {
this.set(key, actions.update(this.get(key)));
} else {
this.set(key, actions.insert());
}
};
Object.setPrototypeOf(BetterMap.prototype, Map.prototype);
Object.setPrototypeOf(BetterMap, Map);
const map = new BetterMap([["a", 1]]);
map.upsert("a", {
update: (value) => value + 1,
insert: () => 1,
});
console.log(map.get("a")); // 2
メモ:
実際、 Reflect.construct() が存在しないため、 ES6 以前のコードへトランスパイルする際には、組み込みオブジェクトを正しくサブクラス化することができません(Error のサブクラス化など)。
ただし、 ES6 のコードを書く場合は、読み取り可能でエラーの可能性が低いクラスと extends の使用を推奨します。
class BetterMap extends Map {
// コンストラクターは既定のコンストラクターのみであるため省略
upsert(key, actions) {
if (this.has(key)) {
this.set(key, actions.update(this.get(key)));
} else {
this.set(key, actions.insert());
}
}
}
const map = new BetterMap([["a", 1]]);
map.upsert("a", {
update: (value) => value + 1,
insert: () => 1,
});
console.log(map.get("a")); // 2
仕様書
| Specification |
|---|
| ECMAScript® 2026 Language Specification # sec-built-in-function-objects |