node js原型对象'self'var不存储回调的正确上下文

时间:2015-11-21 13:42:24

标签: javascript node.js callback prototypal-inheritance

我是经验丰富的开发人员,但对java脚本和nodejs不熟悉,如果这个问题已经“按原样”回答,我很抱歉,但即使我已经查看了多个示例和stackoverflow答案,我也没有找到一个简单的完整示例具有正确的'self'var范围和绑定的原型类(this) 我试过了两个都错了......我将感激你的帮助。 我尝试过 var self = this; 在我的函数声明开始时,但是在运行时,它实际上并没有在设置为原型时通过函数代码,因此,'this'设置不正确。

   /**
     * Module Dependencies
     */
    var cheerio = require('cheerio');
    var http = require('http');

    /**
     * Export
     */

    module.exports = SimplePageGetter;

    function SimplePageGetter(pageLink) {
        this._pageLink = pageLink;
    }

    SimplePageGetter.prototype.getPage = function () {
        var self = this;
        http.request(self._pageLink, self._resultsPageHttpGetCallback).end();
    };

    SimplePageGetter.prototype._resultsPageHttpGetCallback = function (response) {
        var pageBody = '';
        var self = this;
        //another chunk of data has been recieved, so append it to `str`
        response.on('data', function (chunk) {
            pageBody += chunk;
        });

        //the whole response has been recieved, so we just print it out here
        response.on('end', function () {
            self._parsePage(pageBody);
        });
    };

SimplePageGetter.prototype._parsePage = function (body) {
  console.log('page parsed');
}

出于某种原因,调用getPage时'self'是正确的,但是http模块是ClientRequest,而不是_resultsPageHttpGetCallBack上的对象。 我做错了什么?

谢谢,

詹姆斯

1 个答案:

答案 0 :(得分:4)

调用函数中设置self并没有做任何事情来改变被调用函数中this的内容。所以看看这个:

SimplePageGetter.prototype.getPage = function () {
        var self = this;
        http.request(self._pageLink, self._resultsPageHttpGetCallback).end();
    };

这仍然只是将对self._resultsPageHttpGetCallback函数的引用传递给http.requesthttp.request仍将其称为普通函数,而不是方法,因此this中的_resultsPageHttpGetCallback将是未定义的(严格模式)或全局对象(松散模式)。 / p>

self模式用于在同一作用域(或嵌套作用域)中创建的函数,例如:

function someMethod() {
    var self = this;
    http.request(self._pageLink, function(err, data) {
        // Use `self` here to access object info
    }).end();
}

这是有效的,因为我传入http.request 的匿名函数关闭(引用)创建它的上下文,并且该上下文具有self变量,因此该函数可以访问self变量。

对于你正在做的事情,Function#bind会更合适:

SimplePageGetter.prototype.getPage = function () {
        http.request(this._pageLink, this._resultsPageHttpGetCallback.bind(this)).end();
    };

Function#bind创建一个 new 函数,在调用时,将调用原始函数,并将this设置为特定值。

有关this的更多信息:

仅供参考,以下是应用于完整代码示例的Function#bind模式:

/**
 * Module Dependencies
 */
var cheerio = require('cheerio');
var http = require('http');

/**
 * Export
 */

module.exports = SimplePageGetter;

function SimplePageGetter(pageLink) {
    this._pageLink = pageLink;
}

SimplePageGetter.prototype.getPage = function () {
    http.request(this._pageLink, this._resultsPageHttpGetCallback.bind(this)).end();
};

SimplePageGetter.prototype._resultsPageHttpGetCallback = function (response) {
    var pageBody = '';

    response.on('data', function (chunk) {
        pageBody += chunk;
    });

    //the whole response has been recieved, so we just print it out here
    response.on('end', function () {
        this._parsePage(pageBody);
    }.bind(this));
};

SimplePageGetter.prototype._parsePage = function (body) {
    console.log('page parsed');
};

您可以查看ES2015(又名ES6)的新功能,其中许多功能现在可以在v4中使用,因为基础V8引擎支持它们(或者,您可以使用 transpiler 从ES6输入生成ES5代码。

以上是使用ES2015的上述内容:

  • ...箭头函数,它们从定义它们的上下文继承this,使self不必要。

  • ... class关键字,它提供了编写构造函数和原型的更简洁方法。

  • ... let关键字,因为,您知道,这是ES2015代码。 : - )

应用那些:

/**
 * Module Dependencies
 */
let cheerio = require('cheerio');
let http = require('http');

class SimplePageGetter {
    constructor(pageLink) {
        this._pageLink = pageLink;
    }

    getPage() {
        http.request(this._pageLink, response => {
            this._resultsPageHttpGetCallback(response);
        }).end();
    }

    _resultsPageHttpGetCallback(response) {
        let pageBody = '';

        response.on('data', chunk => {
            pageBody += chunk;
        });

        //the whole response has been recieved, so we just print it out here
        response.on('end', () => {
            this.parsePage(pageBody);
        });
    }

    _parsePage(body) {
        console.log('page parsed');
    }
}

/**
 * Export
 */

module.exports = SimplePageGetter;

请注意,class不会像函数声明一样被提升,因此导出的标准位置通常位于模块的底部。如果您只有一个导出(在这种情况下似乎如此),您可以执行

module.exports = class SimplePageGetter {
    //...
};

最后但并非最不重要:除非你真的需要_resultsPageHttpGetCallback_parsePage作为对象的属性(这是公共的),否则我可能会改为使用私有函数,它们接受{{ 1}}实例作为标准参数,或者期望使用SimplePageGetter来调用它,即使它们不是方法。

在这里,他们采取了一个论点:

this

此处,他们希望设置/** * Module Dependencies */ let cheerio = require('cheerio'); let http = require('http'); class SimplePageGetter { constructor(pageLink) { this._pageLink = pageLink; } getPage() { http.request(this._pageLink, response => { resultsPageHttpGetCallback(this, response); }).end(); } } function resultsPageHttpGetCallback(getter, response) { let pageBody = ''; response.on('data', chunk => { pageBody += chunk; }); //the whole response has been recieved, so we just print it out here response.on('end', () => { parsePage(getter, pageBody); }); } function parsePage(getter, body) { console.log('page parsed'); } /** * Export */ module.exports = SimplePageGetter; ,因此我们通过this调用它们:

Function#call