node-js原型对象'self'var没有为回调存储正确的上下文

node js prototype object 'self' var does not store correct context for callback

本文关键字:存储 回调 上下文 对象 原型 self var node-js      更新时间:2023-09-26

我是一名经验丰富的开发人员,但对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上的对象。我做错了什么?

谢谢你,

James

调用函数中设置self不会改变被调用函数中的this。看看这个:

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

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

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创建一个函数,该函数在被调用时将调用this设置为特定值的原始函数。

关于this:的更多信息

  • 关于Stack Overflow: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起就可以在NodeJS中使用,因为底层V8引擎支持它们(或者,您可以使用transiler从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作为对象的属性(它们是公共的),否则我可能会让它们成为私有函数,它们要么接受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:调用他们

/**
 * Module Dependencies
 */
let cheerio = require('cheerio');
let http = require('http');
class SimplePageGetter {
    constructor(pageLink) {
        this._pageLink = pageLink;
    }
    getPage() {
        http.request(this._pageLink, response => {
            resultsPageHttpGetCallback.call(this, response);
        }).end();
    }
}
function 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', () => {
        parsePage.call(this, pageBody);
    });
}
function parsePage(body) {
    console.log('page parsed');
}
/**
 * Export
 */
module.exports = SimplePageGetter;