ES6 позволяет расширять специальные объекты. Таким образом, можно унаследовать от функции. Такой объект можно вызвать как функцию, но как реализовать логику такого вызова?
class Smth extends Function {
constructor (x) {
// What should be done here
super();
}
}
(new Smth(256))() // to get 256 at this call?
Любой метод класса получает ссылку на экземпляр класса через this
. Но когда он вызывается как функция, this
ссылается на window
. Как получить ссылку на экземпляр класса, когда он вызывается как функция?
super(x)
(т.е. передатьFunction
)? Не уверен, чтоFunction
можно действительно продлить.Error
, среди прочего, с проблемами расширения .Function
это просто конструктор функции. Реализация функции должна быть передана конструктору. Если вы не хотите ,Smth
чтобы принять выполнение, вы должны предоставить его в конструкторе, то естьsuper('function implementation here')
.Function
конструктор (среду выполнения), который сильно отличается от функционального выражения (синтаксиса).Ответы:
super
Вызов будет вызыватьFunction
конструктор, который строку кода. Если вы хотите получить доступ к данным вашего экземпляра, вы можете просто жестко его закодировать:но это не очень приятно. Мы хотим использовать закрытие.
Сделать возвращенную функцию закрытием, которое может получить доступ к вашим переменным экземпляра, возможно, но не просто. Хорошо то, что вам не нужно вызывать,
super
если вы не хотите - вы по-прежнему можетеreturn
произвольно использовать объекты из своих конструкторов класса ES6. В этом случае мы бы сделалиНо мы можем сделать еще лучше и абстрагироваться от этого
Smth
:По общему признанию, это создает дополнительный уровень косвенности в цепочке наследования, но это не обязательно плохо (вы можете расширить его вместо родного
Function
). Если вы хотите избежать этого, используйтено обратите внимание, что
Smth
не будет динамически наследовать статическиеFunction
свойства.источник
Smth
экземпляры были такимиinstanceof Smth
(как все ожидали). Вы можете опуститьObject.setPrototypeOf
вызов, если вам не нужен этот или какой-либо из ваших методов-прототипов, объявленных в вашем классе.Object.setPrototypeOf
не так много опасностей при оптимизации, если это делается сразу после создания объекта. Просто если вы измените [[прототип]] объекта взад и вперед в течение его жизни, это будет плохо.this
иreturn
объект.Это подход к созданию вызываемых объектов, которые правильно ссылаются на свои члены объекта и поддерживают правильное наследование, не вмешиваясь в прототипы.
Просто:
Расширьте этот класс и добавьте
__call__
метод, подробнее ниже ...Объяснение в коде и комментариях:
Посмотреть на repl.it
Дальнейшее объяснение
bind
:function.bind()
работает очень похожеfunction.call()
, и у них есть аналогичная сигнатура метода:fn.call(this, arg1, arg2, arg3, ...);
больше на мднfn.bind(this, arg1, arg2, arg3, ...);
больше на мднВ обоих случаях первый аргумент переопределяет
this
контекст внутри функции. Дополнительные аргументы также могут быть привязаны к значению. Но wherecall
немедленно вызывает функцию со связанными значениями,bind
возвращает "экзотический" объект функции, который прозрачно обертывает оригинал, сthis
предустановленными аргументами и любыми другими.Итак, когда вы определяете функцию,
bind
некоторые из ее аргументов:Вы вызываете связанную функцию только с оставшимися аргументами, ее контекст предварительно установлен, в данном случае -
['hello']
.источник
bind
работает (т.е. почему он возвращает экземплярExFunc
)?bind
возвращает прозрачный функциональный объект, который обертывает функциональный объект, для которого он был вызван, то есть наш вызываемый объект, только с повторным вызовомthis
контекста. Таким образом, он действительно возвращает прозрачно завернутый экземплярExFunc
. Сообщение обновлено с дополнительной информацией оbind
.constructor
послеbind
вExFunc
. В подклассах ExFunc доступны все члены. Что касаетсяinstanceof
; в es6 связанные функции называются экзотическими, поэтому их внутренняя работа не очевидна, но я думаю, что он передает вызов своей обернутой цели черезSymbol.hasInstance
. Это очень похоже на прокси, но это простой метод для достижения желаемого эффекта. Их подпись похожа не та.__call__
я не могу получить доступthis.a
илиthis.ab()
. например, repl.it/repls/FelineFinishedDesktopenvironmentВы можете обернуть экземпляр Smth в прокси с помощью
apply
(и, возможно,construct
) ловушки:источник
this
все еще остается пустая функция (проверкаnew Smth().toString()
).setPrototypeOf
и ничего не говорит о прокси. Но я думаю, прокси могут быть такими же проблемными, какsetPrototypeOf
. А ещеtoString
, его можно затенять кастомным методом вSmth.prototype
. В любом случае собственный вариант зависит от реализации.construct
ловушку, чтобы указать поведениеnew new Smth(256)()
. И добавьте пользовательские методы, которые затеняют собственные методы, которые обращаются к коду функции, какtoString
заметил Берги.apply
метод реализован так, как предполагается, или это просто демонстрация, и мне нужно просмотреть дополнительную информациюProxy
иReflect
использовать его надлежащим образом?Я воспользовался советом из ответа Берги и заключил его в модуль NPM .
источник
Обновить:
К сожалению, это не совсем работает, потому что теперь он возвращает объект функции вместо класса, поэтому кажется, что это действительно невозможно сделать без изменения прототипа. Ламе.
По сути, проблема в том, что нет возможности установить
this
значение дляFunction
конструктора. Единственный способ действительно сделать это - использовать.bind
метод впоследствии, однако это не очень удобно для классов.Мы могли бы сделать это в вспомогательном базовом классе, однако
this
он становится доступным только после первоначальногоsuper
вызова, поэтому это немного сложно.Рабочий пример:
(Для примера требуется современный браузер или
node --harmony
.)По сути, базовая функция
ClassFunction
extends будет заключатьFunction
вызов конструктора в пользовательскую функцию, которая похожа на.bind
, но допускает привязку позже, при первом вызове. Затем в самомClassFunction
конструкторе он вызывает возвращаемую функцию, изsuper
которой теперь является связанная функция, переходяthis
к завершению настройки пользовательской функции привязки.Все это довольно сложно, но позволяет избежать изменения прототипа, что считается плохим тоном по причинам оптимизации и может генерировать предупреждения в консолях браузера.
источник
bound
будет ссылаться на функцию, которую выreturn
из этого анонимного класса. Просто назовите его и обратитесь к нему напрямую. Я также рекомендовал бы избегать передачи строк кода, с ними просто беспорядок работать (на каждом этапе процесса разработки).extends
самом деле это не работает так, как ожидалось, посколькуFunction.isPrototypeOf(Smth)
и такжеnew Smth instanceof Function
являются ложными.console.log((new Smth) instanceof Function);
этоtrue
для меня в узле v5.11.0 и последней Firefox.new Smth instanceof Smth
не работает с вашим решением. Кроме тогоSmth
, в ваших экземплярах не будут доступны методы - поскольку вы просто возвращаете стандартFunction
, а не файлSmth
.extend Function
также делаетnew Smth instanceof Smth
ложным.Сначала я пришел к решению
arguments.callee
, но это было ужасно.Я ожидал, что он сломается в глобальном строгом режиме, но похоже, что он работает даже там.
Это был плохой способ из-за использования
arguments.callee
, передачи кода в виде строки и принудительного выполнения в нестрогом режиме. Но потомapply
появилась идея перекрыть .И тест, показывающий, что я могу запускать эту функцию по-разному:
Версия с
на самом деле содержит
bind
функциональность:Версия с
делает
call
иapply
поwindow
несовместимым:поэтому чек следует переместить в
apply
:источник
this
является окном, а не неопределенным, поэтому созданная функция не находится в строгом режиме (по крайней мере, в хроме).Function
это не в строгом режиме. Ужасно, но интересно +1. Однако вы, вероятно, больше не сможете ходить по цепи.Это решение, которое я разработал, которое удовлетворяет все мои потребности в расширении функций и служит мне достаточно хорошо. Преимущества этой техники:
ExtensibleFunction
код идиоматичен для расширения любого класса ES6 (нет, возиться с воображаемыми конструкторами или прокси).instanceof
/.constructor
возвращает ожидаемые значения..bind()
.apply()
и.call()
все работают как положено. Это делается путем переопределения этих методов для изменения контекста «внутренней» функции в отличие отExtensibleFunction
экземпляра (или его подкласса)..bind()
возвращает новый экземпляр конструктора функций (будь то онExtensibleFunction
или подкласс). Он используетсяObject.assign()
для обеспечения того, чтобы свойства, хранящиеся в связанной функции, соответствовали свойствам исходной функции.Symbol
, который может быть запутан модулями или IIFE (или любым другим распространенным методом приватизации ссылок).И без лишних слов код:
редактировать
Так как я был в настроении, я решил, что опубликую для этого пакет на npm.
источник
Существует простое решение, использующее функциональные возможности JavaScript: передать «логику» в качестве аргумента функции конструктору вашего класса, назначить методы этого класса этой функции, а затем вернуть эту функцию из конструктора в качестве результата. :
Вышеупомянутое было протестировано на Node.js 8.
Недостатком приведенного выше примера является то, что он не поддерживает методы, унаследованные от суперклассовой цепочки. Чтобы поддержать это, просто замените «Object. GetOwnPropertyNames (...)» чем-то, что также возвращает имена унаследованных методов. Как это сделать, я полагаю, объясняется в другом вопросе-ответе на Stack Overflow :-). Кстати. Было бы неплохо, если бы ES7 добавил метод для создания имен унаследованных методов ;-).
Если вам нужно поддерживать унаследованные методы, одна из возможностей - добавить статический метод к указанному выше классу, который возвращает все унаследованные и локальные имена методов. Затем вызовите это из конструктора. Если вы затем расширите этот класс Funk, вы также получите унаследованный статический метод.
источник