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

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

我是经验丰富的开发人员,但是 java 脚本和 nodejs 的新手,如果这个问题已经得到回答,我深表歉意 'as-is' 但是即使我已经看过多个示例和 Whosebug 的答案,我还是做到了找不到具有正确 'self' var 作用域和 bind(this) 的原型 class 的简单完整示例。 我都试过了,但都出错了……非常感谢你的帮助。 我试着把 变种自我=这个; 在我的函数声明的开头,但是当 运行 时,当它被设置为原型时它实际上并没有通过函数代码,因此,'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');
}

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

谢谢,

詹姆斯

调用 函数中设置 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 创建一个 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)的新特性,其中许多特性现在可以在 NodeJS 中使用,因为底层 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 作为对象的属性(public),否则我可能会将它们设为私有函数,它要么接受 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;