開個腦洞,如何使用 javascript 實現「仿函數」(Functor)?

Functor

仿函數(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 實現

那麼怎麼實現呢?我以前寫了一篇文章,裏面說 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'

最後,這只是個腦洞!每一個語言都有自身的規律和方法論。不要真的在項目裏這麼寫,除非你的項目目的就是創造漂亮的語法。

相關文章
相關標籤/搜索