Чтобы избежать недоразумений, давайте сначала договоримся о значении некоторых слов. Следующие значения не являются общепринятыми, я предлагаю их только в качестве контекста для этого вопроса.
- функция -- экземпляр
Function
. С ним связана процедура. - object — экземпляр любого другого класса. Не имеет связанной с ним процедуры.
- процедура — блок кода, который можно выполнить. В JS процедуры можно найти только в сочетании с функциями.
- метод — функция, хранящаяся в свойстве другого объекта. Методы выполняются в контексте своих основных объектов.
В JavaScript обычно объекты настраиваются с помощью свойств и методов, а функции — нет. Другими словами, если вы видите это в коде:
foo.bar // Accessing a property on an instance
foo.baz() // Calling a method on an instance
тогда foo
почти наверняка является объектом, а не функцией.
Но технически функции ничем не отличаются от объектов (на самом деле функции в JS считаются объектами, это просто я различаю их для ясности).
И если вы сделаете это:
const foo = function () { /*...*/ }
foo() // Calling the function's procedure
foo.bar // Accessing a property on the function
foo.baz() // Calling a method on the function
... это будет работать нормально.
Я хотел бы ввести еще два термина, чтобы вы могли понять, что я имею в виду. Я не пытаюсь изменить здесь правила JavaScript, просто хочу, чтобы меня поняли.
- function-object – функция, которая используется как объект: имеет свои собственные свойства и/или методы.
- основной метод — процедура самого объекта-функции, в отличие от методов, хранящихся в качестве свойств объекта-функции.
Многие библиотеки JS предлагают объекты-функции, например jQuery и LoDash. т. е. вы можете сделать как $()
, так и $.map()
.
Ладно, теперь к делу.
Я хотел бы иметь возможность создавать свои собственные объекты-функции со следующими требованиями:
- Я должен иметь возможность использовать объявление
class
ES2015 для описания класса моего объекта-функции. Или, по крайней мере, способ описания класса должен быть достаточно удобным. Императивное назначение свойств и методов функции неудобно. - основной метод моего объекта-функции должен выполняться в том же контексте, что и другие его методы.
- основной метод и другие методы должны быть связаны с одним и тем же экземпляром. Например, если у меня есть основной метод и обычный метод, которые оба выполняют
console.log(this.foo)
, то назначениеmyFunctionObject.foo
должно изменить поведение как основного метода, так и другого метода.
Другими словами, я хочу иметь возможность сделать это:
class Foo {
constructor () { this.bar = 'Bar'; }
mainMethod () { console.log(this.bar); }
anotherMethod () { console.log(this.bar); }
}
... но экземпляр Foo
должен иметь foo.mainMethod()
, доступный просто как foo()
.
И выполнение foo.bar = 'Quux'
должно изменить поведение как foo()
, так и foo.anotherMethod()
.
Может быть, есть способ добиться этого с помощью class Foo extends Function
?