Конструкторы классов ES6 нельзя назвать нормальными. Согласно ES6 TypeError
должен быть поднят, когда это будет сделано. Раньше я думал, что классы являются просто синтаксическим сахаром для функции-конструктора + функции в прототипе, но это немного не так.
Мне интересно, в чем причина этого? Если я не пропустил что - то, что мешает вызов функции с обычаем this
, что может быть желательным для некоторых моделей.
в чем причина этого?
Это гарантия. Когда вы вызывали конструктор function
ES5 без new
, он делал очень нежелательные вещи, терпя неудачу. Выброс исключения помогает вам заметить ошибку.
Конечно, они могли бы выбрать синтаксис вызова, чтобы он работал так же, как и строительство, но принудительное использование new
ключевого слова - это хорошая вещь, которая помогает нам легко распознавать экземпляры.
Это предотвращает вызов функции с обычаем
this
, что может быть желательным для некоторых моделей.
Да, это принципиально изменилось в ES6. this
значение инициализируется суперкласса, что позволяет подкласс встроенных команд с внутренними пазами - см здесь для подробностей. Это конфликтует с передачей пользовательского этого аргумента, и для согласованности никогда нельзя допускать этого.
Напомним, что ваши два основных момента:
Конструкторы классов ES6 нельзя назвать нормальными.
Это предотвращает вызов функции с помощью этого
Первое, что нужно отметить, - это то, что с точки зрения поведения класса в классе времени это точки не связаны функционально. Вы могли бы, например, разрешить Foo()
без new
но все же Foo.call({})
ведут себя так, как будто это было new
. Способность вызывать как функция позволяет установить this
, но он не должен, так же Foo.bind({})()
будет свяжите this
, а затем вызвать функцию, но неизбежно this
будет игнорироваться.
Для обоснования решения я не могу дать вам основной источник, но я могу сказать вам, что есть одна веская причина. Синтаксис класса ES6 - это "синтаксический сахар", но не для упрощенного кода, который, вероятно, у вас в голове. Возьмите, к примеру, этот фрагмент, учитывая вашу цель.
class Parent {}
class Child extends Parent {
constructor() {
// What is "this" here?
super();
}
}
Child.call({});
Что делать? В ES6 super()
- это то, что на самом деле устанавливает this
. Если вы попытаетесь получить доступ к this
прежде чем вызвать super()
, он выдает исключение. Ваш пример кода может работать с Base.call({})
, так как он не имеет родительского конструктора, так this
инициализируется фронт, но как только вы вызываете класс ребенка, this
не имеет даже значения фронт. Если вы используете .call
нет места, где можно поставить это значение.
Итак, следующий вопрос: почему дочерние классы не получают this
до super()
? Это связано с тем, что он позволяет синтаксису класса ES6 расширять встроенные типы, такие как Array
Error
и Map
и любой другой встроенный тип конструктора. В стандартном ES5 это было невозможно, хотя с нестандартным __proto__
в ES5 его можно было моделировать грубо. Даже с __proto__
обычно является проблемой производительности для расширения встроенных типов. Включив это поведение в классы ES6, JS-двигатели могут оптимизировать код, так что расширение встроенных типов работает без повышения производительности.
Поэтому для ваших вопросов, да, они могут допускать Foo.call()
или Foo()
, но это должно было бы игнорировать this
любом случае, чтобы разрешить расширение встроенных типов.
Пересмотр спецификации ES6 показывает, как вызов объекта функции класса без нового отключается путем объединения разделов 9.2.9 и 9.2.1:
9.2.9 MakClassConstructor (F)
...
3. Установите внутренний разъем Fs [[FunctionKind]] в "classConstructor".
и при указании метода [[call]] в отличие от метода [[contruct]] для функции:
(9.2.1) 2. Если внутренний слот Fs [[FunctionKind]] "classConstructor", введите исключение TypeError.
Никаких ограничений на функцию вызова в разделе "11.2.3" вызовы функций "ES5.1.
Таким образом, вы ничего не пропустили: вы не можете использовать apply
в функции конструктора классов.
Основное обоснование, вероятно, как для того, чтобы сделать расширения класса довольно строгим упражнением, так и для обнаружения некоторых ранних форм ошибок. Например, вы не можете назвать Promise
, кроме как конструктор - и выход из new
, прежде чем вызов Promise
ошибка программирования. Что касается проходящих классов, обратите внимание, что constructor
свойство экземпляров класса установлено правильно (последний класс после того, как, возможно, несколько расширений) и .prototype
свойства конструктора класса только для чтения - вы не можете динамически изменять прототип объекта, используемый для построить экземпляры класса, даже если вы можете изменить свойство prototype функции-конструктора.
Раньше я думал, что занятия были синтаксическим сахаром, но отошли от концепции.
Reflect.construct
. Что конкретно вы ищете?