new.target
new.target 元屬性允許你檢測函式或建構函式是否使用了 new 運算子進行呼叫。在建構函式和使用 new 運算子呼叫的函式中,new.target 返回對 new 運算子所呼叫的建構函式或函式的引用。在普通的函式呼叫中,new.target 是 undefined。
試一試
function Foo() {
if (!new.target) {
throw new TypeError("calling Foo constructor without new is invalid");
}
}
try {
Foo();
} catch (e) {
console.log(e);
// Expected output: TypeError: calling Foo constructor without new is invalid
}
語法
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() must be called with new");
}
console.log("Foo instantiated with new");
}
new Foo(); // Logs "Foo instantiated with new"
Foo(); // Throws "Foo() must be called with 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() {
// Only way to make the Base() constructor work on the existing
// `this` value instead of a new object that `new` creates.
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,因為 Map() 建構函式不能不帶 new 呼叫。
所有內建建構函式都透過讀取 new.target.prototype 直接構造新例項的整個原型鏈。因此,為了確保 (1) Base 用 new 構造,並且 (2) new.target 指向子類而不是 Base 本身,我們需要使用 Reflect.construct()。
function BetterMap(entries) {
// Call the base class constructor, but setting `new.target` to the subclass,
// so that the instance created has the correct prototype chain.
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 {
// The constructor is omitted because it's just the default one
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
規範
| 規範 |
|---|
| ECMAScript® 2026 語言規範 # sec-built-in-function-objects |
瀏覽器相容性
載入中…