36ve 36ve - 4 months ago 21
Node.js Question

How to properly return a set of instances from a node.js module

I want to instanciate a series of Objects, each one exposing a function, and then return them as a node.js module to be used elsewhere in my project.

I tried using this:

function Role(id, ext) {
this.id = id;
this.ext = ext || [];
}

Role.prototype.filter = function(req, res, next) {
if (!req.user)
return res.status(401).send({
"message": "unauthorized"
});
if (req.user.role !== this.id && this.ext.indexOf(req.user.role) < 0)
return res.status(403).send({
"mesage": "forbidden"
});
return next();
};

var Roles = {
admin: new Role("admin"),
member: new Role("member", ["admin"]) // admin extends member
};

module.exports = Roles;


Which does not work, this.id and this.ext not being defined when filter is called from any instance used in my other module.

Instead, this works:

var Role = function(id, ext) {
var self = this;
self.id = id;
self.ext = ext || [];
self.filter = function(req, res, next) {
if (!req.user)
return res.status(401).send({
"message": "unauthorized"
});
if (req.user.role !== self.id && self.ext.indexOf(req.user.role) < 0)
return res.status(403).send({
"mesage": "forbidden"
});
return next();
};
}


var Roles = {
admin: new Role("admin"),
member: new Role("member", ["admin"]) // admin extends member
};

module.exports = Roles;


... but looks far more unelegant.

What's wrong with the first construct ? I'm pretty new with javascipt oop but it looks pretty similar to what I've seen reading here and there about how to define classes and instances methods with this language.

UPDATE

As per request and for clarification here is how function is called in my other module:

router.get('/', filter, Roles.admin.filter, function(req, res, next) {
// doing some business
});

Answer

this changes its value depending on how the function is invoked.

this keyword works differently in JavaScript compared to other languages. I suggest you to read the guide on MDN website.

In your both your cases this is set to the caller's scope (I suppose is a library like restify or expressjs).

You can verify that doing

Role.prototype.filter = function(req, res, next) {console.log(this)}

In the second case you use self to save the context of this. If you try to use this you will see the value of this is the same of the first example.

So your two examples work in the same way, but in the second you used self = this as workaround.

Workaround

Javascript provides 3 functions to change the value of this, forcing the scope of a function: apply(), call() and bind().

In your case I'm quite sure bind() is the best option.

Somewhere in your code, basing on what library you're using, you have something like:

server.get('/route', Roles.filter);

You need to change it to

server.get('route', Roles.filter.bind(Roles));

Now, without knowing how you call the function I'm quite sure this isn't going to work, but anyway, what you need is pass as argument of bind() an instance of class Roles. In this way this will be set to the value of that instance of Roles.

Differences between bind(), apply() and call()

I said I think you need to use bind() because it is the only one of the three functions that doesn't call the function, but just set the scope. In your case I think you're using a library to provide APIs (due the req, res, next), so you do not want to call the function, just having a reference to it.

call() and apply() instead call the function. The only difference between them is how you pass argument: call() accepts a list of arguments, apply() an array:

function.call(scope, arg1, arg2, arg3);
function.apply(scope, [arg1, arg2, arg3]);
Comments