资讯专栏INFORMATION COLUMN

开个脑洞,如何使用 javascript 实现“仿函数”(Functor)?

anRui / 915人阅读

摘要:中的函数本身就是对象,可以携带自身状态,另外还有化等函数式编程的方法让函数缓存状态,基本上没有仿函数存在的必要。

Functor

仿函数(Functor)是 C++ 里面一个重要的概念,简而言之就是使用重载了 operator() 运算符的对象模仿函数的行为,带来的收益是仿函数可以携带自身状态,普通的 C++ 函数不是对象,做不到这一点。

js 中的函数本身就是对象,可以携带自身状态,另外还有 curry 化等函数式编程的方法让函数缓存状态,基本上没有仿函数存在的必要。最简单的你可以这样写:

function foobar() {
    return foobar.a;
}

foobar.a = 1;
var b = foobar(); // b=>1

这样,foobar 就携带了自身状态 a,并且可以在函数体重访问 a。

但是这里有一个问题:函数体中 foobar.a 这一句是利用闭包实现的,其中 foobar 这个引用被写死了,从效果上看 foobar 成了一个单例。如果我想要多个 foobar 实例怎么办呢?

以及,我比较喜欢 this 指针,而不是闭包。面对这种情况,我更喜欢这样的写法:

// 伪代码

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 上去。

完整的实现如下:

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"

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86418.html

相关文章

  • 理解 Redux

    摘要:我们知道状态机是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。对照上面我们自己写的状态机代码可以看出的作用告诉状态树发生什么变化,及所需要的数据是什么。 前言 我之前开发网站的时候一直用的是 Flux, 自从出了 Redux 之后由于种种原因没有跟进了解,最近手头上的事情基本忙的差不多了,抽空阅读了 Redux 的源码,并整理了这篇博文。 先说重点: Redux 与 R...

    leejan97 评论0 收藏0
  • [译]函数JavaScriptFunctors

    摘要:在我们上面的例子里,的输入和返回值是一样的数据类型,都是。所以我们可以写一个服务于函数的么答案是肯定的但怎么拆开一个函数是个问题简单点儿,你可以直接执行这个函数,然后用她的返回值。我们来拿试一下声明了一个函数的参数及返回值的形态。 Functors 先看看如下代码: function plus1(value) { return value + 1; } 这就是一个普通函数,...

    zone 评论0 收藏0
  • 实现一个奇怪的需求:如何将一串 js 链式调用存储在一个函数或对象中,以备未来调用?

    摘要:简单说,我想实现这么一个功能假设有一个对象,他的方法支持链式调用。本来是立即执行的代码,通过的包装,成了一个函数,我可以把存储起来在任意时刻调用,相当于执行检查。 我相信读到本文标题的人基本上是懵 B 的,我实在想不出更好的表述方法。简单说,我想实现这么一个功能: 假设有一个对象 foobar,他的方法支持链式调用。比如这样: var foobar = new Foobar(); f...

    jerry 评论0 收藏0
  • JavaScript函数式编程(二)

    摘要:函数式编程二拖延症了好久,第二篇终于写出来了。如果你对熟悉的话应该还记得,是可以调用来集中处理错误的对于函数式编程我们也可以做同样的操作,如果运行正确,那么就返回正确的结果如果错误,就返回一个用于描述错误的结果。 JavaScript函数式编程(二) 拖延症了好久,第二篇终于写出来了。 上一篇在这里:JavaScript函数式编程(一) 上一篇文章里我们提到了纯函数的概念,所谓的纯函数...

    booster 评论0 收藏0
  • 小型的编程项目有哪些值得推荐?这本神书写了 22 个,个个了不得

    摘要:电子表格使用语言电子表格是办公软件的必备,我们最熟知的是微软的。文中用框架来实现一个简单的电子表格,所用代码仅行。 showImg(https://segmentfault.com/img/remote/1460000019770011); 本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/Ob... 今天,...

    haitiancoder 评论0 收藏0

发表评论

0条评论

anRui

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<