new.target
new.target
元属性可让你检测是否使用 new
运算符调用函数或构造函数。在使用 new
运算符调用的构造函数和函数中,new.target
返回对调用 new
的构造函数或函数的引用。在正常的函数调用中,new.target
是 undefined
。
¥The new.target
meta-property lets you detect whether a function or constructor was called using the new
operator. In constructors and functions invoked using the new
operator, new.target
returns a reference to the constructor or function that new
was called upon. In normal function calls, new.target
is undefined
.
Try it
语法
值
¥Value
new.target
保证是可构造函数值或 undefined
。
¥new.target
is guaranteed to be a constructable function value or undefined
.
- 在类构造函数中,它指的是调用
new
的类,该类可能是当前构造函数的子类,因为子类通过super()
传递调用超类的构造函数。 - 在普通函数中,如果直接用
new
构造函数,则new.target
指的是函数本身。如果调用该函数时没有使用new
,则new.target
就是undefined
。函数可以用作extends
的基类,在这种情况下new.target
可以指代子类。 - 如果通过
Reflect.construct()
调用构造函数(类或函数),则new.target
引用作为newTarget
传递的值(默认为target
)。 - 在 箭头函数 中,
new.target
是从周围范围继承的。如果箭头函数未在具有new.target
binding 的另一个类或函数中定义,则会引发语法错误。 - 在 静态初始化块 中,
new.target
是undefined
。
描述
¥Description
new.target
语法由关键字 new
、一个点和标识符 target
组成。因为 new
是 保留字,而不是标识符,所以这不是 属性访问器,而是一种特殊的表达式语法。
¥The new.target
syntax consists of the keyword new
, a dot, and the identifier target
. Because new
is a reserved word, not an identifier, this is not a property accessor, but a special expression syntax.
new.target
元属性在所有函数/类主体中均可用;在函数或类之外使用 new.target
是语法错误。
¥The new.target
meta-property is available in all function/class bodies; using new.target
outside of functions or classes is a syntax error.
示例
函数调用中的 new.target
¥new.target in function calls
在普通函数调用(与构造函数调用相对)中,new.target
是 undefined
。这可以让你检测是否使用 new
作为构造函数来调用函数。
¥In normal function calls (as opposed to constructor function calls), new.target
is undefined
. This lets you detect whether a function was called with new
as a constructor.
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 in constructors
在类构造函数中,new.target
指的是被 new
直接调用的构造函数。如果构造函数位于父类中并且是从子构造函数委托的,也会出现这种情况。new.target
指向 new
被调用的类。例如,当使用 new B()
初始化 b
时,打印了 B
的名字;同样,在 a
的情况下,打印了 A
类的名称。
¥In class constructors, new.target
refers to the constructor that was directly invoked by new
. This is also the case if the constructor is in a parent class and was delegated from a child constructor. new.target
points to the class that new
was called upon. For example, when b
was initialized using new B()
, the name of B
was printed; and similarly, in case of a
, the name of class A
was printed.
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()
¥new.target using Reflect.construct()
在 Reflect.construct()
或类之前,通常通过传递 this
的值并让基本构造函数改变它来实现继承。
¥Before Reflect.construct()
or classes, it was common to implement inheritance by passing the value of this
, and letting the base constructor mutate it.
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
,因为没有 new
就无法调用 Map()
构造函数。
¥However, call()
and apply()
actually call the function instead of constructing it, so new.target
has value undefined
. This means that if Base()
checks whether it's constructed with new
, an error will be thrown, or it may behave in other unexpected ways. For example, you can't extend Map
this way, because the Map()
constructor cannot be called without new
.
所有内置构造函数都是通过读取 new.target.prototype
直接构造新实例的整个原型链。因此,为了确保(1)Base
是用 new
构造的,并且(2)new.target
指向子类而不是 Base
本身,我们需要使用 Reflect.construct()
。
¥All built-in constructors directly construct the entire prototype chain of the new instance by reading new.target.prototype
. So to make sure that (1) Base
is constructed with new
, and (2) new.target
points to the subclass instead of Base
itself, we need to use 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
子类化)。¥Note: In fact, due to the lack of
Reflect.construct()
, it is not possible to properly subclass built-ins (likeError
subclassing) when transpiling to pre-ES6 code.
但是,如果你正在编写 ES6 代码,则更喜欢使用类和 extends
,因为它更具可读性且不易出错。
¥However, if you are writing ES6 code, prefer using classes and extends
instead, as it's more readable and less error-prone.
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
规范
Specification |
---|
ECMAScript Language Specification # sec-built-in-function-objects |
浏览器兼容性
BCD tables only load in the browser