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

Javascript 重构:匿名函数到命名函数,参数引用,javascript,node.js,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

所以我有点进退两难,试图在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);
            });
        });
    });
};

将一个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)设置上下文(即显式绑定/定义此现在实际位于函数内部而不是调用函数时的内容)和2)执行curry所做的操作。如果你不需要或不想要第一个功能,那么我认为更干净的方法是使用咖喱。(如果您通常不使用像lodash这样的库,很容易找到一个可以添加到本地UTIL中的独立curry函数。)因此,这个问题有更多关于这个主题的内容:

这是我在使用
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);
};

要么将其设置为全局变量,要么将其作为参数传递。如果它们需要访问闭包变量,我认为让它们嵌套没有什么错。您仍然可以命名它们。那
foo
来自哪里?您使用的是什么promise库?看起来你可以改进你的承诺模式。您的闭包没有问题。@Bergi告诉我将所有闭包重构为命名函数:-我正在使用bluebird for
Promise
如果您使用的外部库中事件回调函数已经有一组预定义参数,如何修改它?我粘贴了一个实际的示例,因此如果您需要将回调函数传递到
池。acquire
,并且回调的参数只有
err
client
,那么您可以在别处编写一个名为
foo
的函数,该函数包含6个参数,
query,params,env,res,err,client
,然后显式传递到
pool.acquire
的回调函数将是一个函数,它是
foo
w.r.t.到前四个参数的循环,如下所示:
pool.acquire(u.curry(foo)(query)(params)(env)(res))我使用
bind()
解决了我的问题,我不知道使用
curry
i+1你的答案有什么好处。但是我更喜欢
bind
方法。如果你写一点详细的回答,包括
bind
并解释为什么
curry
可能比
bind
更好,我会接受你的回答。很好的一点,我添加了一些关于bind和curry的注释