new.target

Baseline 已廣泛支援

此功能已成熟,可跨多種裝置和瀏覽器版本使用。自 2017 年 9 月以來,它已在瀏覽器中提供。

new.target 元屬性允許你檢測函式或建構函式是否使用了 new 運算子進行呼叫。在建構函式和使用 new 運算子呼叫的函式中,new.target 返回對 new 運算子所呼叫的建構函式或函式的引用。在普通的函式呼叫中,new.targetundefined

試一試

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
}

語法

js
new.target

new.target 保證是一個可構造的函式值或 undefined

  • 在類建構函式中,它指向被 new 呼叫的類,這可能是一個當前建構函式的子類,因為子類透過 super() 間接呼叫其父類的建構函式。
  • 在普通函式中,如果函式直接用 new 構造,new.target 指向函式本身。如果函式不帶 new 呼叫,new.targetundefined。函式可以作為 extends 的基類,在這種情況下,new.target 可能指向子類。
  • 如果建構函式(類或函式)透過 Reflect.construct() 呼叫,那麼 new.target 指向作為 newTarget 傳遞的值(預設為 target)。
  • 箭頭函式 中,new.target 繼承自周圍的作用域。如果箭頭函式未定義在另一個具有 new.target 繫結 的類或函式中,則會丟擲語法錯誤。
  • 靜態初始化塊 中,new.targetundefined

描述

new.target 語法由關鍵字 new、一個點和識別符號 target 組成。由於 new 是一個 保留字,而不是識別符號,所以這不是一個 屬性訪問器,而是一種特殊的表示式語法。

new.target 元屬性在所有函式/類主體中都可用;在函式或類之外使用 new.target 會導致語法錯誤。

示例

函式呼叫中的 new.target

在普通的函式呼叫中(與建構函式呼叫相對),new.targetundefined。這使你能夠檢測函式是否作為建構函式與 new 一起呼叫。

js
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 的名稱。

js
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 的值,並讓基建構函式對其進行修改來實現在繼承。

js
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) Basenew 構造,並且 (2) new.target 指向子類而不是 Base 本身,我們需要使用 Reflect.construct()

js
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,因為它更具可讀性且不易出錯。

js
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

瀏覽器相容性

另見