Можно ли создать функтор в JavaScript?
Я пытаюсь создать функцию, которая содержит состояние, но вызывается с помощью foo().
Возможно ли это?
Ответы
Ответ 1
Я считаю, что это то, что вы хотите:
var foo = (function () {
var state = 0;
return function () {
return state++;
};
})();
Или, следуя пример Википедии:
var makeAccumulator = function (n) {
return function (x) {
n += x;
return n;
};
};
var acc = makeAccumulator(2);
alert(acc(2)); // 4
alert(acc(3)); // 7
JavaScript - один из тех языков, который имеет, ИМХО, отличную поддержку функций как граждан первого класса.
Ответ 2
Поскольку функции Javascript являются объектами первого класса, это способ сделать это:
var state = 0;
var myFunctor = function() { alert('I functored: ' + state++);};
Переменная state
будет доступна для функции myFunctor
в ее локальном закрытии. (Глобальный в этом примере). Другие ответы на этот вопрос имеют более сложные примеры.
Здесь нет эквивалента только "реализации operator()" на каком-либо существующем объекте.
Ответ 3
Вы можете рассматривать функции как объекты и давать им "переменные-члены". Здесь мы используем внутреннюю функцию в fact
, но вместо того, чтобы просто объявлять ее как локальную переменную (var loop = ...
), мы помещаем ее определение вне функции, используя синтаксис объекта (fact.loop = ...
). Это позволяет существенно "экспортировать" внутреннюю функцию loop
fact
, чтобы ее можно было повторно использовать функцией doubleFact
.
var fact = function(n) {
return fact.loop(n, 1);
};
fact.loop = function(n, acc) {
if (n < 1) {
return acc;
} else {
return fact.loop(n-1, acc * n);
}
};
var doubleFact = function(x) {
return fact.loop(x * 2, 1);
};
console.log(fact(5)); // 120
console.log(doubleFact(5)); // 3628800
Та же идея может быть использована для поддержания состояния.
var countCalled = function() {
console.log("I've been called " + (++countCalled.callCount) + " times.");
};
countCalled.callCount = 0;
countCalled(); // I've been called 1 times.
countCalled(); // I've been called 2 times.
countCalled(); // I've been called 3 times.
Если вы хотите иметь возможность создавать несколько экземпляров, каждый со своим собственным состоянием, попробуйте следующее:
var CallCounter = function(name) {
var f = function() {
console.log(name + " has been called " + (++f.callCount) + " times.");
};
f.callCount = 0;
return f;
};
var foo = CallCounter("foo");
var bar = CallCounter("bar");
foo();
foo();
bar();
foo();
bar();
bar();
bar();
console.log(foo.callCount);
console.log(bar.callCount);
Выходы:
foo has been called 1 times.
foo has been called 2 times.
bar has been called 1 times.
foo has been called 3 times.
bar has been called 2 times.
bar has been called 3 times.
bar has been called 4 times.
3
4
Ответ 4
Вы можете использовать функцию возврата с закрытыми свойствами объекта функции.
Параметры такого функтора (замыкания) могут быть изменены после инициализации,
которые могут быть полезны при построении некоторых вычислений, которые могут быть запущены/настроены позже. Взгляните на пример ниже. Этот ответ похож на limp_chimp.
function functor() {
var run = function runX() {
return runX.functorParam;
// or:
// return run.functorParam;
};
run.functorParam = 'functor param'; // default value
return run;
}
var f1 = functor();
// call f1
f1(); // 'functor param'
// lets change functor parameters:
f1.functorParam = 'Hello';
// call f1
f1(); // 'Hello'