CARVIEW |
このページはコミュニティーの尽力で英語から翻訳されました。MDN Web Docs コミュニティーについてもっと知り、仲間になるにはこちらから。
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> |
ブラウザーの互換性
Loading…