2

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?

2 Answers 2

2

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...

Sign up to request clarification or add additional context in comments.

4 Comments

Is this contained in a node.js module? AKA lib/myrequest.js or somesuch? Or does the code you pasted live in your main application?
I think this is along the right track, and provides some good clues, but it doesn't quite work for me.
I'm guessing that the 'request' module returns the object constructor, not an actual object, which is why your code works for you, but not for me. I had to use var foo = require('foo.js'); Bar.prototype = Object.create( foo.__proto__ ), but was able to get roughly the same result as you, after some trial and error. Thank you!
@Flimzy correct. Often I wish the JS/node docs would be a bit more clear about what is a Class, what is a Constructor / factory, and what is an instance of an object.
0

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"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.