27

I want to create modules to structure my NodeJS application, but I'm a little lost, and I haven't found anything (with hours of searching) that is completely definitive on the subject.

Say I'd like to create a "user" module, from which I can create new users in my code using something like:

var newUser = new User();

Ideally, I'd require my module at the top of my code using something like:

var User = require('../lib/user');

This works great. The question is, how should I structure the user module? Is the following the best way?

module.exports = function User()    {
    var authorized = false;
    var username = undefined;
    var password = undefined;
    var statistics = undefined;

    this.authorized = function()  {
        return authorized;
    }
    this.username = function()  {
        return username;
    }
    this.statistics = function()    {
        return statistics;
    }
}

I'm writing getters and setters for my various module variables, allowing me to hide things I don't want to accidentally access from other code. However, I have done it this way before:

function User() {
    this.authStatus = false;
    this.email;
    this.displayName;
    this.inSession;
}

User.prototype.isAuthenticated = function() {
    return(this.authStatus && this.email && this.displayName)
}

User.prototype.isInSession = function() {
    return(this.inSession && this.isAuthenticated());
}

exports.User = User;

This works too, with one caveat; I haven't found a way to access the user properties from within closures. If my understanding is correct, with the second implementation, I can't. This means if I need to hand a function off to a db library as a callback to edit the user's properties, I can't. That'd look something like:

User.prototype.login = function()    {
    db.doDbStuff('get user data query', function(_error, _result)    {
         this.username = _result[0].name; //this code will not work
    });
}

The code doesn't work, to my understanding, because the "this" keyword is within the scope of the closure, not the User. Even if the code were to be placed within the User function:

function User() {
    this.login = function()    { //you know

It wouldn't work.

I guess my question is, what's the best solution to this problem? Is it the method I presented in the first code block? That seems rather cumbersome and messy and prone to variable collision. I'm scared.

Thanks in advance!

Yossale
  • 14,165
  • 22
  • 82
  • 109
Matt Egan
  • 809
  • 1
  • 10
  • 16
  • 2
    If any of this didn't make sense, I apologize in advance, I just took some pain medicine for my wisdom teeth and I feel kinda light and funky. – Matt Egan Feb 14 '12 at 00:25
  • 1
    Seems to bleed more into the realm of javascript architecture rather than than to do with node.js in general. http://speakerdeck.com/u/addyosmani/p/scaling-your-javascript-applications and http://www.slideshare.net/SlexAxton/superclassy-inheritance-in-javascript are quite good. – balupton Feb 14 '12 at 01:21
  • It's worth noting that in both of your examples the variables ARE NOT private. You should take a look at: http://javascript.crockford.com/private.html – Don Scott Jan 14 '17 at 23:53
  • I think the page Don refers to has moved to https://crockford.com/javascript/private.html – dcorking Sep 19 '18 at 10:49

3 Answers3

26

I typically go with the second approach, attaching functions to the prototypes.

The issue you're having with variables "not being available in closures" has nothing to do with prototypes. You'd have that same issue either way you structure it.

It's to do with javascript's oft-confusing dynamic this: http://robotlolita.me/2011/10/09/understanding-javascript-oop.html#sec-2-1

Basically, you need to do something like:

User.prototype.login = function()    {
    var self = this // bind this to self so you have a reference to what `this` was

    db.doDbStuff('get user data query', function(_error, _result)    {
         self.username = _result[0].name; // self refers to the original `this`, so this works.
    });
}

You also have the option of using function.bind: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind

Within the bound function, the value of this will be whatever value you provided to .bind(value):

User.prototype.login = function()    {
    db.doDbStuff('get user data query', (function(_error, _result)    { 
         this.username = _result[0].name; // bind fixes the original `this`, so this also works.
    }).bind(this));
}

Whether you use function.bind or self = this is somewhat of a personal taste question, but we were doing some benchmarks in the freenode#nodejs the other day and discovered bind() is 20 something times slower than var self = this.

As to your original question about how to structure modules, there are so many examples to learn from on github. Simply find your favourite module and inspect how they structure it. I notice that many people seem to prefer factories over exposing constructors directly (e.g. require('module').create() ). Your call.

timoxley
  • 5,156
  • 3
  • 36
  • 40
  • 1
    Oh. That's so brilliant and simple! Jeeze. -- Thanks! – Matt Egan Feb 14 '12 at 00:55
  • The dynamic this is really confusing when it changes unexpectedly, but it's also one of Javascript's most powerful features when you actually use it on purpose. – timoxley Feb 14 '12 at 00:58
  • Yeah, Javascript gives me this really weird vibe of extremely powerful and awesome combined with "oh my god this is ridiculous." -- Is there any way you'd recommend to keep some members private using the prototype method? – Matt Egan Feb 14 '12 at 00:59
  • @Matt: You could go with Python's approach: prefix it with an underscore and use that as a convention that "this is private; don't touch this unless you know what you're doing". – icktoofay Feb 14 '12 at 01:02
  • 3
    Just don't add them to the exports/this object and they will be private. You can use `bind()` to call the methods on the current object. But be aware that module.require will always return the same object so these vars are essentially 'static'. The harder question is how to create private instance variables. I think most people opt for the simpler 'this._thisIsPrivate' approach (ugly but works), but you can also do it like so: http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-private-variables-in-javascript/ – timoxley Feb 14 '12 at 01:10
  • This question was asked/responded to in 2012; An update to this question from the current state of JavaScript in 2016: you can use arrow functions `() => {}` if you are writing in ES6 (available when using `strict mode` in node.js). `this` will not be rebound to the current closure, but instead reference the parent `this`; more here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions – jpschroeder Dec 18 '15 at 15:21
13

As a different approach, I am a fan of the following pattern.

module.exports = function User(data) {

    //this properly private stuff.

    var privateVar;
    var username = data.username;
    var pass = data.pass; //etc

    function privateFunc() {
    }

    return {
        login: function(cb) {
            db.doStuff(username, pass, cb);
        }
    };
};
user1978317
  • 343
  • 2
  • 6
Maleck13
  • 1,709
  • 15
  • 17
  • 4
    I like this too. But this approach only works for singletons, right? You can't instantiate this way. – owzim Nov 10 '13 at 21:14
  • 2
    @owzim This pattern is actually not only for singletons, it looks very similar to module.exports = (function(){ ... })(); which is a single pattern because it executes the function straight away. But the code in this answer is not a singleton, because you can now do require('data')(my_instance_data).myInstanceFunction(); – Vegard Jul 16 '15 at 15:34
2
User.prototype.login = function()    {
    var _this = this;
    db.doDbStuff('get user data query', function(_error, _result)    {
        _this.username = _result[0].name; //this code will now work
    });
}

The 'this' you used was outside its scope, it was the callback 'this'.

Amol M Kulkarni
  • 21,143
  • 34
  • 120
  • 164
Alon Valadji
  • 628
  • 4
  • 8