在 ES5 中判断函数如何被调用

在 ES5 中判断函数是不是使用了 new 来调用(即作为构造器),最流行的方式是使用 instanceof , 例如:

function Person(name) { if (this instanceof Person) { this.name = name; // 使用 new } else { throw new Error("You must use new with Person.") } } var person = new Person("Nicholas"); var notAPerson = Person("Nicholas"); // 抛出错误

此处对 this 值进行了检查,来判断其是否为构造器的一个实例:若是,正常继续执行;否 则抛出错误。这能奏效是因为 [[Construct]] 方法创建了 Person 的一个新实例并将其赋值 给 this 。可惜的是,该方法并不绝对可靠,因为在不使用 new 的情况下 this 仍然可能 是 Person 的实例,正如下例:
function Person(name) { if (this instanceof Person) { this.name = name; // 使用 new } else { throw new Error("You must use new with Person.") } } var person = new Person("Nicholas"); var notAPerson = Person.call(person, "Michael"); // 奏效了!

调用 Person.call() 并将 person 变量作为第一个参数传入,这意味着将 Person 内部的 this 设置为了 person 。对于该函数来说,没有任何方法能将这种方式与使用 new 调用区 分开来。
new.target 元属性 为了解决这个问题, ES6 引入了 new.target 元属性。元属性指的是“非对象”(例如 new ) 上的一个属性,并提供关联到它的目标的附加信息。当函数的 [[Construct]] 方法被调用 时, new.target 会被填入 new 运算符的作用目标,该目标通常是新创建的对象实例的构造 器,并且会成为函数体内部的 this 值。而若 [[Call]] 被执行, new.target 的值则会是 undefined 。
【在 ES5 中判断函数如何被调用】通过检查 new.target 是否被定义,这个新的元属性就让你能安全地判断函数是否被使用 new 进行了调用。
function Person(name) { if (typeof new.target !== "undefined") { this.name = name; // 使用 new } else { throw new Error("You must use new with Person.") } } var person = new Person("Nicholas"); var notAPerson = Person.call(person, "Michael"); // 出错!

使用 new.target 而非 this instanceof Person , Person 构造器会在未使用 new 调用时 正确地抛出错误。 也可以检查 new.target 是否被使用特定构造器进行了调用,例如以下代码:
function Person(name) { if (new.target === Person) { this.name = name; // 使用 new } else { throw new Error("You must use new with Person.") } } function AnotherPerson(name) { Person.call(this, name); } var person = new Person("Nicholas"); var anotherPerson = new AnotherPerson("Nicholas"); // 出错!

在此代码中,为了正确工作, new.target 必须是 Person 。当调用 new AnotherPerson(“Nicholas”) 时, Person.call(this, name) 也随之被调用,从而抛出了错误, 因为此时在 Person 构造器内部的 new.target 值为 undefined ( Person 并未使用 new 调用)。

    推荐阅读