Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

What would be the best (performance, memory wise) to achieve a clean way to create a class or some way to properly structure the code, that shares 2 variables (req, res) which have a decent object size.

And yes, for those who use Node.js it are the req and res variables, but it is irrelevant.

This is what I tried so far:

function Client(req, res){
  var self = this;

  self.req = req;
  self.res = res;

  route.call(self);
  handleRoute.call(self);
  handleRequestData.call(self);
}

function route(){
  var self = this;

  self.req.route = // ...
}

function handleAuth(){
  var self = this;

  self.req.auth = // ...
}

function handleRequestData(){
  var self = this;

  self.req.data = // ...
}

I am wondering if it could be improved, since I mentioned req and res are pretty decent objects with methods, properties. Since .call(self) you pass through the instance, would it be most effective way?

Also I don't like using "var self = this;" all the time, it is useless.

And btw, I don't want to use Coffeescript.

Solution 1. (fails because of the massive passing through req and res)

Lets assume we got to files 1. client.js

var Client = {
  route: function(req, res){},
  auth: function(req, res, callback){ callback(req, res); },
  getData: function(req, res, callback){ callback(req, res); } // async
}

file 2. main.js

var client = require('./client.js');

client.route(req, res); // fine because this is syncronous.

// This is where the problem is, massive passing through req and res parameters.
client.auth(req, res, function(req, res){
  client.getData(req, res, function(req, res){
    // Do something...
  });
});
share|improve this question
add comment

4 Answers

I don’t know why you are constantly saving the context (this) in a local variable (self). You might as well use this all the way, unless you need to bring it into a local object with different scope.

Also, you can chain the other methods in the Prototype to gain access to the context inside them:

function Client(req, res){

  this.req = req;
  this.res = res;

  this.route();
  this.handleRoute();
  this.handleRequestData();
}

Client.prototype.route = function(){
  this.req.route = // ...
}

Client.prototype.handleAuth = function(){
  this.req.auth = // ...
}

Client.prototype.handleRequestData = function(){
  this.req.data = // ...
}

If you don’t want "public" methods, just do:

function Client(req, res){

  this.req = req;
  this.res = res;

  route.call(this);
  handleRoute.call(this);
  handleRequestData.call(this);
}

function route(){
  this.req.route = // ...
}

function handleAuth(){
  this.req.auth = // ...
}

function handleRequestData(){
  this.req.data = // ...
}
share|improve this answer
    
private methods? the constructor does cascading, I don't want to be able to call client.handleAuth(); –  onlineracoon Aug 21 '12 at 20:36
    
OK, you should probably mention that. BTW that doesn’t explain why you are putting this into self... –  David Aug 21 '12 at 20:37
    
Some functions are asyncronous, and redefine the "this" variable, but I still want to be able to refer to the actual instance of the class. –  onlineracoon Aug 21 '12 at 20:39
    
Well that would be up to each function to deal with it’s context I guess. You could always pass it as an argument to save yourself some extra lines of code. –  David Aug 21 '12 at 20:50
add comment

Are you intending to reuse the route, handleAuth, and handleRequestData functions? They could be "private" to the Client constructor function:

function Client(req, res) {
    function route() {
        //...
    }

    function handleAuth() {
        //...
    }

    function handleRequestData() {
        //...
    }

    route();
    handleRoute();
    handleRequestData();
}

See that I didn't set req and res as this members. I don't know if this is mandatory in your case. Also, this is just the beginning of what could be done; as stated in the comments to this answer, every time a new Client is created, new instances of the private functions are created as well, wasting resources.

A more sophisticated approach could define Client using a self-invoking function expression:

var Client = (function() {
    function route(req, res) {
        //...
    }

    function handleAuth(req, res) {
        //...
    }

    function handleRequestData(req, res) {
        //...
    }

    // This is the constructor function:
    return function(req, res) {
        route(req, res);
        handleRoute(req, res);
        handleRequestData(req, res);
    }
})();

Here, Client is defined as the product of the function expression enclosed in the outermost parenthesis. The function expression returns the constructor function, which has access to the closure functions route, handleRoute, and handleRequestData. Note that each of these functions is defined with req and res parameters. This way, they behave like static private functions, and you can code and use them regardless of what's referenced by this.

About self = this: it's known that JavaScript may be very confusing about the this keyword. Some information here. So, sometimes it's convenient to assign this to a closure variable named like self or me. It should not be done blindly though... as anything after all.

share|improve this answer
2  
I would not suggest this if you are planning on creating multiple Client objects. There will be multiple copies of the route, handleAuth, etc. functions, one for each instance. David's suggestion will deal better with this. –  Scott Sauyet Aug 21 '12 at 20:42
1  
You are right, @ScottSauyet. The code I posted may not be suitable for production. I'll expand my answer. –  Humberto Aug 21 '12 at 20:44
add comment

Note: I assumed the Client function would be used a constructor, e.g.

var clientInstance = new Client();
clientInstance.req;//access req

I think I may have been wrong about that.

Objects are passed via reference, so they shouldn't be taking up more memory than the bytes that go into linking to another namespace/scope. By passing in as local parameters, you've probably eliminated overhead for the call object compared to if they were referenced from outside of your function constructor but you could probably build dozens of similar objects without seeing a major impact on memory. (although I don't know what the last three lines do - they might create new object instances with properties that are passed by value which would hit memory harder).

The advantage of function constructors is that you can build factories where vars that would be passed as values are instead accessed via closure. In JIT compilers at least, I wouldn't expect those to take up more space in memory either. Example:

function objFactory(){

    var someHandyProperty = 42;

    function ObjConstructor(){
        var internalHandyProperty = someHandyProperty;
        this.publicMethodAlert = function(){ alert('The answer to the ultimate question: ' + internalHandyProperty);

    }

    return new ObjConstructor()

}

var someInstance = objFactory();
someInstance.publicMethodAlert();//alerts 42 referenced via closure

For a bigger example, have a look at jQuery. Nearly all of a JQ objects methods and values are accessed via a hand-me-down prototype or closures. JQ objects themselves carry a massive array of methods and properties around but don't take up much memory at all.

In the example above, defining the 'publicMethodAlert' in the prototype of the constructor would be even more efficient since I'm not redefining a function for every instance.

Example:

function objFactory(){

    var someHandyProperty = 42;


    function ObjConstructor(){
    }

    ObjConstructor.prototype.publicMethodAlert = function(){ alert('The answer to the ultimate question: ' + someHandyProperty);

    return new ObjConstructor()

}

var someInstance = objFactory();
someInstance.publicMethodAlert();//still alerts 42 referenced via closure

Now the instances just builds from an empty function with a prototype fallback (always the same function) so we're not redefining a function for every instance (note: in non-JIT interpreters we might actually redefine the function for every firing of the factory, but I would expect JITs, which I'm certain Node uses, to cache everything defined locally in this case)

One of the things I personally dislike about CoffeeScript is reduced flexibility in the manner in which I go about building my objects and defining my functions.

share|improve this answer
    
thanks for the answer, tried some more, changed my question –  onlineracoon Aug 21 '12 at 21:12
add comment

Why not simply

function Client(req, res){
    this.req = req;
    this.res = res;

    this.route();
    this.handleRoute();
    this.handleRequestData();
}

Client.prototype = = {
    route: function() {
        this.req.route = // ...
    },

    handleAuth: function() {
        this.req.auth = // ...
    },

    handleRequestData: function() {
        this.req.data = // ...
    }
};

You would call it like this:

var client = new Client(req, res);

Javascript does not have classes. But constructor functions used this way are a reasonable approximation for many uses.


EDIT based on note that functions should be private:

var Client = (function() {
    var route = function() {
        this.req.route = // ...
    },

    handleAuth = function() {
        this.req.auth = // ...
    },

    handleRequestData = function() {
        this.req.data = // ...
    };

    return function(req, res) {
        this.req = req;
        this.res = res;

        route.call(this);
        handleRoute.call(this);
        handleRequestData.call(this);
    } 
}());

The functions are available now only to the constructor function. If other functions need access to them, they would have to be defined inside the same closure.

share|improve this answer
    
Thing is, route, handleAuth and handleRequestData should be private, not public. –  onlineracoon Aug 21 '12 at 21:28
    
Added another version that handles this. –  Scott Sauyet Aug 22 '12 at 11:45
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.