重构:命名函数的匿名函数,参数引用

时间:2015-08-27 02:28:12

标签: javascript node.js

所以我遇到了一些困境,试图在Javascript中重构一组嵌套的匿名闭包。但是,我不确定维护对参数的标准方式,否则将被子闭包看到。

例如

var foo;
var obj = new Obj();
obj.foobar(function (p1, p2, p3) {
    foo.onEvent(function (s1, s2) {
         if (s1 === 'bar') {
              s2.status = p2; //p2 has a reference here
         }
    });  
});

但是如果我重构这段代码,那么每个关闭它自己命名的函数,获取p2的引用的理想方法是什么?

例如

var onFooEvent = function (s1, s2) {
             if (s1 === 'bar') {
                  s2.status = p2; //p2 will no longer has a reference here
             }
        }; 

var onFoobar = function (p1, p2, p3) {
        foo.onEvent(onFooEvent); 
    };

var foo;
var obj = new Obj();
obj.foobar(onFoobar);

更健全的例子

var pooledQuery = function (query, params, env, res) {
    return new imports.promise(function(resolve, reject) {
        pool.acquire(function (err, client) {
            var db;
            if (err != null) {
                console.log(err)
                reject(err);
            }
            var meta = { env: env, res: res, result: [] };                

            db = client.query(query, params);
            db.on('result', function (res) {
                return res.on('row', function (row) {
                    return meta.result.push(row);
                }).on('error', function (err) {
                    meta.result = err;
                    reject(meta);
                }).on('end', function (info) {
                    return meta.result;
                });
            }).on('end', function () {
                pool.release(client);
                resolve(meta);
            });
        });
    });
};

2 个答案:

答案 0 :(得分:1)

将curried函数传递给foo.onEvent(并另外更改onFooEvent接受的参数) - 类似这样的事情:

var onFooEvent = function (pArgs, s1, s2) {
  if (s1 === 'bar') {
    s2.status = pArgs[1]; 
  }
}; 

var onFoobar = function (p1, p2, p3) {
  foo.onEvent(_.curry(onFooEvent)(arguments)); 
};

这是一个人为的可运行的例子:

var _ = require('lodash')

var print_stuff = function (p_args, s1, s2) {
  console.log(p_args[0])
  console.log(p_args[1])
  console.log(p_args[2])
  console.log(s1)
  console.log(s2)
}

var do_theoretical_stuff = function (p1, p2, p3) {
  return _.curry(print_stuff)(arguments)
}

var do_actual_stuff = do_theoretical_stuff(1, 2, 3) 

do_actual_stuff('a', 'b')
输出到控制台的

1
2
3
a
b

更新:下面关于bind的确非常好。 bind_.curry之间的区别在于,使用bind时,必须在使用它时设置上下文(通过第一个参数)。 bind基本上是为你做两件事:1)设置上下文(即,显式绑定/定义this实际上将在函数内部而不是在调用函数时 2做咖喱做的事。如果你不需要或想要第一个功能,那么我认为更简洁的方法是使用咖喱。 (如果你通常不使用像lodash这样的库,很容易找到一个可以添加到本地工具中的独立咖喱功能。)这个问题在主题上有更多:Javascript 's bind vs curry?

答案 1 :(得分:1)

这是我在使用bind时的解决方法,这里有更多的重构,我打算将所有函数封装在另一个类实例中。 (但忽略那部分)只是想分享我是如何解决它的。

var DbPool = function () {

};

DbPool.prototype.onRow = function (meta, row) {
    return meta.result.push(row);
};

var onRowError = function (meta, reject, err) {
    meta.result = err;
    reject(meta);
};

var onRowEnd = function (meta, info) {
    return meta.result;
};

var onResult = function (meta, reject, res) {

    var onRowBind = DbPool.prototype.onRow.bind(this, meta);
    var onRowErrorBind = onRowError.bind(this, meta, reject);
    var onRowEndBind = onRowEnd.bind(this, meta);

    return res.on('row', onRowBind).on('error', onRowErrorBind).on('end', onRowEndBind);
};

var onEnd = function (meta, resolve, client) {
    pool.release(client);
    resolve(meta);
};

var dbPoolAcquired = function (query, params, env, res, resolve, reject, err, client) {
    var db;
    if (err != null) {
        console.log(err)
        reject(err);
    }
    var meta = { env: env, res: res, result: [] };

    logger.debug("Query %s Params %s", query, params);

    db = client.query(query, params);

    var onResultBind = onResult.bind(this, meta, reject);
    var onEndBind = onEnd.bind(this, meta, resolve, client);

    db.on('result', onResultBind).on('end', onEndBind);
};

var dbPoolPromised = function(query, params, env, res, resolve, reject) {
    var dbPoolAcquiredBind = dbPoolAcquired.bind(undefined, query, params, env, res, resolve, reject)
    pool.acquire(dbPoolAcquiredBind);
};

var pooledQuery = function (query, params, env, res) {
    var dbPoolPromisedBind = dbPoolPromised.bind(this, query, params, env, res);
    return new imports.promise(dbPoolPromisedBind);
};