2009-07-14 13 views
15

私は状態を保持するが、foo()で呼び出される関数を作ろうとしています。
可能ですか?JavaScriptでファンクタを作ることは可能ですか?

+2

ジョエル・スポルスキはjavascriptのファンクタで本当に素晴らしい記事があります。http://www.joelonsoftware.com/items/2006/08/01.html – peirix

+0

この記事では、ファンクタではなく、それは機能している。 –

+0

@torazaburo Javascriptにはファンクタがありません。しかし、同じC++機能はオブジェクトであるため、関数を使ってエミュレートできます。 –

答えて

28

私は、これはあなたが望むものであると信じて:

var foo = (function() { 
    var state = 0; 

    return function() { 
     return state++; 
    }; 
})(); 

または、the Wikipedia exampleを次

var makeAccumulator = function (n) { 
    return function (x) { 
     n += x; 
     return n; 
    }; 
}; 

var acc = makeAccumulator(2); 

alert(acc(2)); // 4 
alert(acc(3)); // 7 

JavaScriptが持っているこれらの言語の1、私見、第一級オブジェクトとして機能するための優れたサポートであります。

+0

それは未定義を2回返します。 –

+0

"それ"。 foo()の例は実際にはundefinedを返します。それはまさに例であった。アキュムレータの例は期待どおりに動作するはずです。単純なユースケースを追加しました。 –

+0

ありがとうございます。 fooの例私は2番目の例の前にコメントしました。 –

4

JavaScript関数がファーストクラスのオブジェクトであるので、これはそれを行う方法です:

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

state変数は、そのローカルクロージャーでmyFunctor機能が利用できるようになります。 (この例ではグローバル)。この質問に対する他の回答には、より洗練された例があります。

既存のオブジェクトに「operator()」を実装するのと同じことはありませんが、

2

関数をオブジェクトとして扱い、それらに "メンバ変数"を与えることができます。ここでは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 
-1

あなたがしてクロージャを返す関数を使用することができますが添付された関数オブジェクトのプロパティ このファンクタ(閉包)のパラメータは、初期化後に変更することができます。 は、後で実行/設定できる計算を構築する際に役立ちます。以下の例を見てください。この回答は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'

関連する問題