仿函數(Functor)是 C++ 裏面一個重要的概念,簡而言之就是使用重載了 operator() 運算符的對象模仿函數的行爲,帶來的收益是仿函數能夠攜帶自身狀態,普通的 C++ 函數不是對象,作不到這一點。編程
js 中的函數自己就是對象,能夠攜帶自身狀態,另外還有 curry 化等函數式編程的方法讓函數緩存狀態,基本上沒有仿函數存在的必要。最簡單的你能夠這樣寫:api
function foobar() { return foobar.a; } foobar.a = 1; var b = foobar(); // b=>1
這樣,foobar 就攜帶了自身狀態 a,而且能夠在函數體重訪問 a。緩存
可是這裏有一個問題:函數體中 foobar.a 這一句是利用閉包實現的,其中 foobar 這個引用被寫死了,從效果上看 foobar 成了一個單例。若是我想要多個 foobar 實例怎麼辦呢?閉包
以及,我比較喜歡 this 指針,而不是閉包。面對這種狀況,我更喜歡這樣的寫法:app
// 僞代碼 function foobar() { return this.b; } foobar.setB = function (val) { this.b = val } var foo = new foobar; foo.setB(1); var b = foo(); // b=>1
那麼怎麼實現呢?我以前寫了一篇文章,裏面說 js 不容易實現相似的概念。可是當時我沒細想,今天試了一下其實變更一下接口,仍是能實現相似效果的。函數式編程
基本的原理就是這樣:函數
function f() {...} var functor = f.bind(f);
讓一個函數 bind 它本身,這樣它不就能用 this 訪問本身了嗎?可是這裏還有個問題,bind 的返回結果並非 f 自身而是另外一個函數,functor 的持有者在外部訪問不到 f。因此這裏還要用 js 的新 api defineProperty 處理一下,使得對 functor 的某些屬性訪問,轉移到 f 上去。this
完整的實現以下:prototype
function makeFunctor(fn, props) { function thisFn() { return fn.apply(this, Array.prototype.slice.call(arguments)); } var ret = thisFn.bind(thisFn); for (var key in props) { if (!props.hasOwnProperty(key)) { continue; } Object.defineProperty(ret, key, { configurable : true, enumerable : true, get : function () { return thisFn[key]; }, set : function (value) { thisFn[key] = value; } }); ret[key] = props[key]; } return ret; }
經過 makeFunctor,咱們能夠經過一個函數 fn 建立不少個 functor,每個都有自身的狀態,互不影響。而且在 fn 中咱們可使用 this 訪問自身狀態。好比:指針
function hello () { alert('Hello, ' + this.name); } hello.create = function () { makeFunctor(hello, { name : 'Tom' }); } var ftHello = hello.create(); var ftHello2 = hello.create(); ftHello(); // Hello, Tom' ftHello.name = 'Jack'; ftHello(); // Hello, Jack' ftHello2(); // Hello, Tom'
最後,這只是個腦洞!每一個語言都有自身的規律和方法論。不要真的在項目裏這麼寫,除非你的項目目的就是創造漂亮的語法。