Making a node.js module that either takes an argument or doesn't, and returns an appropriate function/object

StackOverflow https://stackoverflow.com/questions/23662787

Question

I'm not sure if it's possible, but I'll still ask whether it is.

I want to make a module that could be used in either one of the two ways :

  1. Like this:

    MyModule = require('mymodule');
    MyModule.do('stuff');
    modules outputs stuff
  2. Or like this: (called with an additional argument)

    MyModule = require('mymodule')('some');
    MyModule.do('stuff');
    modules outputs "some" stuff

I tried to make it liks so

function MyModuleThatDoesntTakeArguments(argument){
    function MyModuleThatTakesArgument(){
        this.argument = argument;
    }
    // this will be called when arguments are passed
    MyModuleThatTakesArgument.prototype.do = function(){
        console.log('outputs', argument, 'stuff');
    }

    // this* will be called when no arguments are passed
    this.do = function(){
        console.log('outputs stuff');
    }

    return MyModuleThatTakesArgument;
}
// or this* will be called when no arguments   ″     ″
MyModuleThatDoesntTakeArguments.prototype.do = MyModuleThatDoesntTakeArguments.do; 

module.exports = MyModuleThatDoesntTakeArguments;

It works when called with an argument (#1) but gives an error like "Function Object doesn't have a method do".

I'm realizing the basic problem is that it can either be called as a constructor function (when called with argument (#1)) which makes it return the inner function., But when called without the argument (#2) it just returns the constructor itself, which unfortunately can't have its method (this.do or .prototype.do) called and thus gives that error.

So I guess the whey I'm writing it it's not possible.

Is it still possible some other way to achieve what I want?

Was it helpful?

Solution

You will need to make the constructor function an "instance" with a do method as well. Assuming you want it to be a constructor (that constructs objects with a common prototype), the pattern might look like this:

function MyConstructor(prefix) {
    this.pref = prefix ? prefix + " " : "";
}
MyConstructor.prototype.do = function(str) {
    console.log(this.pref + str);
}

// now make it an "instance":
MyConstructor.call(MyConstructor, "");
utils.merge(MyConstructor.prototype, MyConstructor);

module.exports = MyConstructor;

Now you can call it either

var mod = new MyConstructor("some");
mod.do("stuff");

or

var mod = MyConstructor
mod.do("stuff");

You can make a similar thing for factories. Notice that if you want to insert if (!(this instanceof MyConstructor) return new MyConstructor(prefix); to make it work without new, instead of initialising the "instance" with MyConstructor.call(MyConstructor, ""); you will need to manually do MyConstructor.pref = "";

OTHER TIPS

Remember, functions are objects in JavaScript. You can add a do method to the function object!

function MyModule(initParam)
{
    this.initParam = initParam;
}

MyModule.do = MyModule.prototype.do = function(arg)
{
    if(this instanceof MyModule)
        console.log(this.initParam + " " + arg);
    else
        console.log(arg);
}

module.exports = MyModule;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top