Setting self
in the calling function doesn't do anything to change what this
will be in the function being called. So looking at this:
SimplePageGetter.prototype.getPage = function () {
var self = this;
http.request(self._pageLink, self._resultsPageHttpGetCallback).end();
};
That's still just passing a reference to the self._resultsPageHttpGetCallback
function to http.request
. http.request
will still call it as just a normal function, not a method, and so this
in _resultsPageHttpGetCallback
will be either undefined (strict mode) or the global object (loose mode).
The self
pattern use useful for functions created in the same scope (or a nested scope), for instance:
function someMethod() {
var self = this;
http.request(self._pageLink, function(err, data) {
// Use `self` here to access object info
}).end();
}
That works because the anonymous function I'm passing into http.request
closes over (has a reference to) the context where it's created, and that context has the self
variable, and so the function can access the self
variable.
For what you're doing, Function#bind
would be more appropriate:
SimplePageGetter.prototype.getPage = function () {
http.request(this._pageLink, this._resultsPageHttpGetCallback.bind(this)).end();
};
Function#bind
creates a new function that, when called, will call the original function with this
set to a specific value.
More about this
:
Just for reference, here's the Function#bind
pattern applied to your complete code example:
/**
* 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');
};
You might look into the new features of ES2015 (aka ES6), many of which you can use in NodeJS now as of v4 because the underlying V8 engine supports them (alternately, you can use a transpiler to produce ES5 code from ES6 input).
Here's the above using ES2015's:
...arrow functions, which inherit this
from the context in which they're defined, making self
unnecessary.
...the class
keyword, which provides a more concise way to write constructors and prototypes.
...the let
keyword, just because, you know, it's ES2015 code. :-)
Applying those:
/**
* 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;
Note that class
is not hoisted like function declarations, so the standard place for exporting is usually at the bottom of the module. If you have just the one export (as you seem to in this case), though, you could do
module.exports = class SimplePageGetter {
//...
};
Last but not least: Unless you really need _resultsPageHttpGetCallback
and _parsePage
to be properties on the object (which are public), I would probably make them private functions instead, which either accept the SimplePageGetter
instance as a standard argument, or expect to be called with this
referring to it even though they aren't methods.
Here, they take an argument:
/**
* 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;
Here, they expect this
to be set, so we call them via 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;