new.target
Baseline Широко доступно
Эта функция хорошо зарекомендовала себя и работает на многих устройствах и версиях браузеров. Она доступна в браузерах с марта 2016 года.
Мета-свойство 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"
new.target с использованием Reflect.construct()
До 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(), невозможно корректно подклассировать встроенные объекты (например, подклассы Error) при трансляции в код до ES6.
Однако, если вы пишете код 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
Спецификации
Совместимость с браузерами
| Настольные | Мобильные | Сервер | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Opera | Safari | Chrome Android | Firefox for Android | Opera Android | Safari on iOS | Samsung Internet | WebView Android | WebView on iOS | Bun | Deno | Node.js | |
new_target |
46 |
13 |
41 |
33 |
11 |
46 |
41 |
33 |
11 |
5.0 |
46 |
11 |
1.0.0 |
1.0 |
5.0.0 |
См. также
© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target