new.target
new.target
元属性允许您检测函数或构造函数是否使用 new
运算符调用。在使用 new
运算符调用的构造函数和函数中,new.target
返回对 new
被调用的构造函数或函数的引用。在普通函数调用中,new.target
为 undefined
。
试一试
语法
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
为 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 语言规范 # sec-built-in-function-objects |
浏览器兼容性
BCD 表格仅在浏览器中加载