我是一位经验丰富的开发人员,但是对Java脚本和Node.js还是陌生的,如果这个问题已按原样回答,我深表歉意,但是即使我遍历了多个示例和stackoverflow答案,也没有找到原型类的简单完整示例正确的'self'变量作用域和绑定(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上的对象。
请问我做错了什么?

谢谢 ,

詹姆士

最佳答案

在调用函数中设置self并不会改变被调用函数中this的内容。所以看这个:

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


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

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的更多信息:


在堆栈溢出中:How does the this keyword work?
在我贫乏的小博客上:Mythical Methods | You Must Remember 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)的新功能,因为底层V8引擎支持它们,所以您现在可以从v4开始在NodeJS中使用其中的许多功能(或者,您可以使用编译器从ES6输入生成ES5代码)。

以下是使用ES2015的内容:


... arrow函数,它们从定义它们的上下文继承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;

09-09 21:26
查看更多