Что такое "new.target"?

В спецификации ECMAScript 2015 упоминается ключевое слово (или слова?) new.target ровно 3 раза - 1 раз в 14.2.3:

Обычно, Содержит не выглядит внутри большинства форм функций. Однако, Содержит используется для обнаружения new.target, этого и суперпотребления в пределах ArrowFunction.

и дважды в 14.2.16:

Функция ArrowFunction не определяет локальные привязки для аргументов, супер, это, или new.target. Любая ссылка на аргументы, супер, это или new.target внутри функции ArrowFunction необходимо разрешить привязку в лексически окружающая среда

MDN упоминает об этом, но очень расплывчато и страница неполна.

Вавилон, похоже, не поддерживает его. Я получил синтаксические ошибки при попытке использовать new.target в функции (стрелка или другие).

Что это такое и как оно должно использоваться?

Ответ 1

Вы не нашли его в спецификации, потому что в определениях синтаксиса он написан с пробелами, как new . target. Имя выражения NewTarget, и вы найдете этот термин несколько раз.

NewTarget является первым из так называемых метаданных и может быть найден в разделе 12.3.8.

Его единственная цель - извлечь текущее значение значения [[NewTarget]] для текущей (не-стрелочной) функции, Это значение, которое задается при вызове функции (очень похоже на привязку this) и согласно §8.1.1.3 Записи функциональной среды:

Если эта запись среды была создана внутренним методом [[Construct]], [[NewTarget]] является значением параметра [[Construct]] NewTarget. В противном случае его значение равно undefined.

Итак, с одной стороны, наконец, позволяет нам определить, была ли функция вызвана как конструктор или нет.

Но это не настоящая цель. Так что же тогда? Это часть того, как классы ES6 представляют собой не только синтаксический сахар, но и то, как они позволяют нам наследовать от встроенных объектов. Когда вы вызываете конструктор class через new X, значение this еще не инициализировано - объект еще не создан при вводе тела конструктора. Он создается супер конструктором во время вызова super() (что необходимо при создании внутренних слотов). Тем не менее, экземпляр должен наследовать от .prototype изначально называемого конструктора и что там, где newTarget входит в игру. Он удерживает "внешний" конструктор, который получил вызов new во время вызовов super(). Вы можете следовать за ней полностью в спецификации, но в основном это NewTarget не выполняемый в данный момент конструктор, который передается в OrdinaryCreateFromConstructor процедура - например, в шаг 5 из §9.2.2 [[Construct]] для пользовательских функций.

Длинный текст, возможно, пример лучше подходит:

class Parent {
    constructor() {
        // implicit (from the `super` call)
        //    new.target = Child;
        // implicit (because `Parent` doesn't extend anything):
        //    this = Object.create(new.target.prototype);
        console.log(new.target) // Child!
    }
}
class Child extends Parent {
    constructor() {
        // `this` is uninitialised (and would throw if accessed)
        // implicit (from the `new` call):
        //    new.target = Child 
        super(); // this = Reflect.construct(Parent, [], new.target);
        console.log(this);
    }
}
new Child;

Ответ 2

Он предназначен в первую очередь как лучший способ обнаружить, когда конструктор вызывается без new.

Из http://www.2ality.com/2015/02/es6-classes-final.html:

new.target - неявный параметр, который имеет все функции. Он должен конструировать вызовы, к которым this относится к вызовам методов.

Поэтому я могу написать:

function Foo() {
  if (!new.target) throw "Foo() must be called with new";
  ...
}

Более подробная информация о том, как это работает, и дополнительные контексты, в которых она полезна, но мы оставим ее здесь.

Для некоторых заметок, касающихся new.target, см. https://esdiscuss.org/notes/2015-01-27.