如何使用 Node.js 的库(如 async 和 request)解决“this”问题

9
我已经编写了一个node脚本,通过请求REST API数据(使用request库)来获取一些数据。它由一些函数组成,就像这样:
var data = { /* object to store all data */ },
function getKloutData() {
  request(url, function() { /* store data */}
}
// and a function for twitter data

因为我想在获取所有数据后执行一些操作,所以我使用了async库来运行所有的fetch函数,代码如下:

async.parallel([ getTwitterData, getKloutData ], function() {
    console.log('done');
});

这一切都很顺利,但是我想把所有内容放在一个对象模式中,这样我就可以同时获取多个账户:
function Fetcher(name) { 
    this.userID = ''
    this.user = { /* data */ }
    this.init();
}
Fetcher.prototype.init = function() {
    async.parallel([ this.getTwitterData, this.getKloutData ], function() {
        console.log('done');
    });
}
Fetcher.prototype.getKloutData = function(callback) {
    request(url, function () { /* store data */ });
};

由于async和request改变了this上下文,所以此方法无法正常工作。我唯一能想到的解决办法就是将所有通过async和request传递的内容绑定起来:

Fetcher.prototype.init = function() {
    async.parallel([ this.getTwitterData.bind(this), this.getKloutData.bind(this) ], function() {
        console.log('done');
    });
}
Fetcher.prototype.getKloutData = function(callback) {
    function saveData() {
        /* store data */
    }


    request(url, saveData.bind(this);
};

我是不是做了一些基本错误或者其他的事情?我认为回滚脚本并将其分叉到子进程会创建太多的开销。

3个回答

9

您做得非常正确。

另一种方法是始终在上下文中保留对象的引用而不使用bind,但这需要一些技巧:

Fetcher.prototype.init = function() {
    var self = this;
    async.parallel([
        function(){ return self.getTwitterData() },
        function(){ return self.getKloutData() }
    ], function() {
        console.log('done');
    });
}

Fetcher.prototype.getKloutData = function(callback) {
    var self = this;

    function saveData() {
        // store data
        self.blah();
    }

    request(url, saveData);
};

您可以事先进行绑定:
Fetcher.prototype.bindAll = function(){
    this.getKloutData = this.prototype.getKloutData.bind(this);
    this.getTwitterData = this.prototype.getTwitterData.bind(this);
};

Fetcher.prototype.init = function(){
    this.bindAll();
    async.parallel([ this.getTwitterData, this.getKloutData ], function() {
        console.log('done');
    });
};

3
Underscore库有一个方便的函数叫做bindAll,可以使这个过程变得不那么痛苦。如果您选择使用CoffeeScript,您可以使用箭头函数定义方法,这样就不需要进行任何显式绑定了。 - Jimmy

3
你可以将这个保存到另一个变量中:
var me = this;

那么就是你的this

0
使用此函数实例化对象:
function newClass(klass) {
    var obj = new klass;

    $.map(obj, function(value, key) {
        if (typeof  value == "function") {
            obj[key] = value.bind(obj);
        }
    });

    return obj;
}

这将自动绑定所有函数,因此您将以习惯的面向对象编程风格获得对象, 当对象内的方法具有其对象的上下文时。

因此,您不通过以下方式实例化对象:

var obj = new Fetcher();

但是:

var obj = newClass(Fetcher);

我的原始问题是在Node.js环境中,jQuery没有意义。此外,Underscore和Lodash中的bindAll方法(适用于所有JS环境的库)旨在为您完成此操作。 - askmike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接