Ответ 1
Я надеюсь, что я понимаю это право.
Я считаю, что вы хотите, чтобы функтор являлся экземпляром предопределенного прототипа (да, класса, а не классического класса), а также напрямую вызываемого? Правильно? Если это так, то это имеет смысл и очень мощный и гибкий (особенно в очень асинхронной среде, такой как JavaScript). К сожалению, нет способа сделать это изящно в JavaScript без манипуляции __proto__
. Вы можете сделать это, разложив анонимную функцию и скопировав все ссылки на все методы (которые, как представляется, направляют вас на курс), чтобы действовать как прокси-класс. Недостатками этого являются...
- Это очень дорогостоящее с точки зрения времени выполнения.
-
(functorObj instanceof MyClass)
никогда не будетtrue
. - Свойства не будут доступны напрямую (если все они были назначены по ссылке, это будет другая история, но примитивы назначаются по значению). Это можно решить с помощью аксессуаров с помощью
defineProperty
или просто названных методов доступа, если это необходимо (кажется, что это то, что вы ищете, просто добавьте все свойства к функтору с помощьюdefineProperty
через getters/setters вместо просто функций if вам не нужна поддержка кросс-движков/обратная совместимость). - Вероятно, вы столкнетесь с крайними случаями, когда конечные собственные прототипы (например, Object.prototype или Array.prototype [если вы наследуете это]) могут работать не так, как ожидалось.
- Вызов
functorObj(someArg)
всегда сделает объектthis
объектом, независимо от того, вызвал ли онfunctorObj.call(someOtherObj, someArg)
(это не относится к вызовам методов, хотя) - Поскольку объект-функтор создается во время запроса, он будет заблокирован во времени, и манипуляция с исходным прототипом не повлияет на выделенные объекты-объекты, такие как нормальный объект (изменение MyClass.prototype не повлияет на какие-либо объекты-функторы и верно и обратное).
Если вы используете его мягко, хотя, ничто из этого не должно быть большой проблемой.
В вашем прототипе вашего класса определите что-то вроде...
// This is you're emulated "overloaded" call() operator.
MyClass.prototype.execute = function() {
alert('I have been called like a function but have (semi-)proper access to this!');
};
MyClass.prototype.asFunctor = function(/* templateFunction */) {
if ((typeof arguments[0] !== 'function') && (typeof this.execute !== 'function'))
throw new TypeError('You really should define the calling operator for a functor shouldn\'t you?');
// This is both the resulting functor proxy object as well as the proxy call function
var res = function() {
var ret;
if (res.templateFunction !== null)
// the this context here could be res.asObject, or res, or whatever your goal is here
ret = res.templateFunction.call(this, arguments);
if (typeof res.asObject.execute === 'function')
ret = res.asObject.execute.apply(res.asObject, arguments);
return ret;
};
res.asObject = this;
res.templateFunction = (typeof arguments[0] === 'function') ? arguments[0] : null;
for (var k in this) {
if (typeof this[k] === 'function') {
res[k] = (function(reference) {
var m = function() {
return m.proxyReference.apply((this === res) ? res.asObject : this, arguments);
};
m.proxyReference = reference;
return m;
})(this.asObject[k]);
}
}
return res;
};
Результирующее использование будет выглядеть примерно так...
var aobj = new MyClass();
var afunctor = aobj.asFunctor();
aobj.someMethodOfMine(); // << works
afunctor.someMethodOfMine(); // << works exactly like the previous call (including the this context).
afunctor('hello'); // << works by calling aobj.execute('hello');
(aobj instanceof MyClass) // << true
(afunctor instanceof MyClass) // << false
(afunctor.asObject === aobj) // << true
// to bind with a previous function...
var afunctor = (new MyClass()).asFunctor(function() { alert('I am the original call'); });
afunctor() // << first calls the original, then execute();
// To simply wrap a previous function, don't define execute() in the prototype.
Вы даже можете связать бесчисленное количество других объектов/функций/и т.д., пока корова не вернется домой. Просто отредактируйте запрос прокси.
Надеюсь, что это поможет. О, и, конечно, вы можете изменить поток factory так, чтобы конструктор, вызываемый без оператора new
, затем создавал новый объект и возвращал объект-функтор. Однако вы предпочитаете (вы могли бы сделать это и другими способами).
Наконец, чтобы любая функция стала исполняющим оператором для функтора в немного более элегантной форме, просто сделайте прокси-функцию методом Function.prototype
и передайте ей объект для обертывания, если вы хотите сделать что-то вроде (вам придется поменять templateFunction
на this
и this
с аргументом, конечно)...
var functor = (function() { /* something */ }).asFunctor(aobj);