Я надеюсь, что я правильно понимаю.
Я полагаю, вам нужен функтор, который одновременно является экземпляром предопределенного прототипа (да, класса, но не классического класса), а также может вызываться напрямую? Правильно? Если это так, то это имеет смысл и является очень мощным и гибким (особенно в сильно асинхронной среде, такой как JavaScript). К сожалению, нет способа сделать это элегантно в JavaScript без манипулирования __proto__
. Вы можете сделать это, выделив анонимную функцию и скопировав все ссылки на все методы (что, по-видимому, является направлением, которым вы руководили), чтобы действовать как прокси-класс. Недостатками этого являются ...
- Это очень дорого с точки зрения времени выполнения.
(functorObj instanceof MyClass)
никогда не будет true
.
- Свойства не будут доступны напрямую (если бы они были все назначены по ссылке, это была бы другая история, но примитивы назначались по значению). Это может быть решено с помощью методов доступа через
defineProperty
или просто с помощью именованных методов доступа, если необходимо (кажется, это то, что вы ищете, просто добавьте все свойства к функтору с помощью defineProperty
через методы получения / установки вместо просто функций, если вам не нужна поддержка кросс-движка / обратная совместимость).
- Скорее всего, вы столкнетесь с крайними случаями, когда конечные собственные прототипы (такие как Object.prototype или Array.prototype [если вы наследуете это]) могут работать не так, как ожидалось.
- вызов
functorObj(someArg)
будет всегда сделает контекст this
объектом, независимо от того, вызывается ли он functorObj.call(someOtherObj, someArg)
(хотя это не относится к вызовам методов)
- Поскольку объект functor создается во время запроса, он будет заблокирован во времени, и манипулирование исходным прототипом не повлияет на выделенные объекты functor, как это будет затронуто для обычного объекта (изменение MyClass.prototype не повлияет на объекты functor и верно и обратное).
Если вы используете его осторожно, ничего из этого не должно иметь большого значения.
В вашем прототипе вашего класса определите что-то вроде ...
// 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.
Вы могли бы даже связать цепь бесчисленными другими объектами / функциями / и т.д., пока коровы не вернутся домой. Просто немного измени рефакторинг вызова прокси.
Надеюсь, это поможет. Да, и, конечно, вы можете изменить фабричный поток так, чтобы конструктор, вызываемый без оператора new
, затем создавал новый объект и возвращал объект функтор. Однако вы предпочитаете (вы могли бы сделать это и другими способами).
Наконец, чтобы любая функция стала оператором выполнения для функтора более элегантным способом, просто сделайте прокси-функцию методом Function.prototype
и передайте ей объект для переноса, если вы хотите сделать что-то вроде (вам нужно поменять местами templateFunction
с this
и this
с аргументом, конечно) ...
var functor = (function() { /* something */ }).asFunctor(aobj);