开个脑洞,如何使用 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'

最后,这只是个脑洞!每一个语言都有自身的规律和方法论。不要真的在项目里这么写,除非你的项目目的就是创造漂亮的语法。

相关文章
相关标签/搜索