Чтобы проверить является ли функция конструкторной, нужно посмотреть есть ли у нее поле [[Construct]]
. Нет ни единого варианта проверить это напрямую.
Но есть два способа, которые мне известны:
- Проверить поля, которые мы можем просмотреть с помощью кода js (быстрее).
- Проверить поле
[[Construct]]
, используя интерфейсы, которые это делают (медленнее).
К первому типу относится реализация prototype.js
, ко второму newTarget.js
, bind.js
, proxy.js
и reflect.js
.
Также существует одна весомая разница; при проверке в newTarget.js
, bind.js
, proxy.js
и reflect.js
мы используем [[Construct]]
поле, которое вызывается через операцию Construct для создания объекта, чтобы убедиться, что объект создан, тем самым это докажет, что функция является конструкторной, но в этом и опасность если произойдет ошибка в алгоритме [[Construct]]
проверяемая функция не пройдет проверку.
- [[Construct]] для встроенных функций (пример: Object, Function, Proxy и т.д.)
- [[Construct]] для функций определенных внутри кода ECMAScript (это функции объявленные программистом на js)
- [[Construct]] для привязанных функций (функции созданные через Function.prototype.bind)
- [[Construct]] для прокси объектов (функции созданные через Proxy)
Первый тип же более безопасен он не запускает никакие функции, а просто смотрит наличие полей, которые должны быть у функции конструктора, в этом тоже есть своя опасность, так как отсутствие некоторых полей попросту сломает проверку.
Но есть Proxy
! В чем же разница между вариантами newTarget.js
, bind.js
, reflect.js
и proxy.js
? Разница последнего в том, что когда происходит вызов [[Construct]]
, применение происходит не у тестируемой функции, а у обертки proxy
, которая имеет собственное поле [[Construct]]
. Спецификация определяет разные алгоритмы для обычного функционального объекта и объекта proxy
с ловушкой construct
. Классические вызовы обычных функциональных объектов через вызов оператора new
будут проверять поле [[Construct]]
и если его нет - будет выбрасываться ошибка, к proxy
это правило также применяется, однако тут у proxy
есть преимущество и все из-за того что происходит вызов другого [[Construct]]
, который впоследствии вызовет специальный метод-ловушку construct
у proxy
(преимущество в том что тестируемая функция может содержать ошибки внутри себя; через proxy
можно не запускать тестируемую функцию, тем самым избегая ненужной нам ошибки, которая появляется в других способах).
Вывод: Самый рабочий и безотказный вариант с Proxy
, он отрабатывает по полю [[Construct]]
проверяемого значения и в случае если значение не оказывается конструктором выбрасывает ошибку. Первый вариант с просмотром ключевых свойств быстрый, но минус в том что можно подделать поля и проверка будет взламываемой, что плохо.