Reputation: 79594
I've been primarily a Perl coder for years, but also have a background in C++, so I'm coming from a "classical" OO background, and now learning node.js. I just read through The Principles of Object-Oriented JavaScript, which did a good job of explaining the JS concept of OO to classically-minded people like me. But I'm left with a question, specifically related to node.js and inheritance. Pardon me if I'm still using "classical" vocabulary to explain my problem.
Lets suppose I have a module lib/foo.js
:
function foo() {
console.log('Foo was called');
}
module.exports.foo = foo;
And I want to "subclass" this in another module lib/bar.js
:
var foo = require('foo.js');
// Do some magic here with *.prototype, maybe?
function bar() {
console.log('Bar was called');
}
module.exports.bar = bar;
Such that my main script can do this:
var bar = require('lib/bar.js');
bar.foo(); // Output "Foo was called"
bar.bar(); // Output "Bar was called"
Is this even possible? If so, what am I missing?
Or is this an anti-pattern? Or plain impossible? What should I do instead?
Upvotes: 2
Views: 3666
Reputation: 79594
For reference, the specific solution I came up with to my sample code problem follows:
In lib/foo.js
:
var Foo = function() {}
Foo.prototype.foo = function() {
console.log('Foo was called!');
};
module.exports = new Foo;
In lib/bar.js
:
var foo = require('./foo.js');
var Bar = function() {}
Bar.prototype = Object.create(foo.__proto__);
Bar.prototype.constructor = Foo;
Bar.prototype.bar = function() {
console.log('Bar was called!');
};
module.exports = new Bar;
Then in my test script:
var bar = require('lib/bar.js');
bar.foo(); // Output "Foo was called"
bar.bar(); // Output "Bar was called"
Upvotes: 0
Reputation: 15729
Here's how I did it, to override one method in the request module. Warning: many node modules are poorly designed for extension, including request, as they do way too much stuff in the constructor. Not just a gazillion argument options, but starting up IO, connections, etc. For example, request does the http connection (eventually) as part of the constructor. There is no explicit .call()
or .goDoIt()
method.
In my example, I wanted to use querystring instead of qs to format forms. My module is cleverly named "MyRequest". In a separate file named myrequest.js you have:
var Request = require('request/request.js');
var querystring = require('querystring');
MyRequest.prototype = Object.create(Request.prototype);
MyRequest.prototype.constructor = MyRequest;
// jury rig the constructor to do "just enough". Don't parse all the gazillion options
// In my case, all I wanted to patch was for a POST request
function MyRequest(options, callbackfn) {
"use strict";
if (callbackfn)
options.callback = callbackfn;
options.method = options.method || 'POST'; // used currently only for posts
Request.prototype.constructor.call(this, options);
// ^^^ this will trigger everything, including the actual http request (icky)
// so at this point you can't change anything more
}
// override form method to use querystring to do the stringify
MyRequest.prototype.form = function (form) {
"use strict";
if (form) {
this.setHeader('content-type', 'application/x-www-form-urlencoded; charset=utf-8');
this.body = querystring.stringify(form).toString('utf8');
// note that this.body and this.setHeader are fields/methods inherited from Request, not added in MyRequest.
return this;
}
else
return Request.prototype.form.apply(this, arguments);
};
Then, in your application, instead of
var Request = require("request");
Request(url, function(err, resp, body)
{
// do something here
});
you go
var MyRequest = require("lib/myrequest");
MyRequest(url, function(err, resp, body)
{
// do that same something here
});
I'm not a JavaScript guru so there may be better ways...
Upvotes: 2