Можно ли сделать функтор в JavaScript? - PullRequest
14 голосов
/ 14 июля 2009

Я пытаюсь создать функцию, которая хранит состояние, но вызывается с помощью foo ().
Возможно ли это?

Ответы [ 4 ]

28 голосов
/ 14 июля 2009

Я считаю, что это то, что вы хотите:

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 - один из тех языков, который, IMHO, отлично поддерживает функции первоклассных граждан.

4 голосов
/ 19 сентября 2009

Поскольку функции Javascript являются объектами первого класса, это способ сделать это:

var state = 0;
var myFunctor = function() { alert('I functored: ' + state++);};

Переменная state будет доступна для функции myFunctor при ее локальном закрытии. (Глобальный в этом примере). Другие ответы на этот вопрос имеют более сложные примеры.

Тем не менее, нет эквивалента просто «реализации operator ()» для некоторого существующего объекта.

2 голосов
/ 19 ноября 2013

Вы можете рассматривать функции как объекты и присваивать им «переменные-члены». Здесь мы используем внутреннюю функцию в 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
0 голосов
/ 13 апреля 2016

Вы можете использовать функцию возврата замыкания с прикрепленными свойствами объекта функции. Параметры такого функтора (замыкания) могут быть изменены после инициализации, который может быть полезен при построении некоторых вычислений, которые можно запустить / настроить позже. Посмотрите на пример ниже. Этот ответ похож на 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'
...