Friday, December 19, 2014

Stop trying to add class structure to JavaScript

Recently, I read an interesting article titled The Two Pillars of Javascript, and one of the points in the article that I found interesting was the part about trying to create classes in JavaScript. Stop trying to create class structure in JavaScript? But, I thought classes and OOP are how we should be programming, plus classes are everywhere in .NET, Java and Ruby.

The article talks about how JavaScript is not like other languages and how we should embrace the "objects without classes" and "anonymous functions (Lambdas)" structure that it provides.

I only bring this up because after reading that article, I realized, that in Angular I'm unconsciously doing this already. With AngularJS you never use the "prototype" or "new" keywords, because your only registering objects or constructor functions.

Ok, lets step back, what are we talking about?
Here is how we create an object using class like functionality as shown in a previous post:
var Car = function(color) {
    this._wheels = 4;
    this._condition = 'good';
    this.initialize(color);
}
Car.prototype.initialize = function(color) {
    this._color = color;
    ...
};
Car.prototype.drive = function(speed) {};

//create a car and drive
var myCar = new Car(#bada55);
myCar.drive(90);

//and then extend Car and create a Truck
var Truck = function(color){
    this._type = 'pickup';
    this.initialize(color);
}
var tmp = Truck.prototype = new Car();
Truck.prototype.Car_initialize = tmp.initialize;
Truck.prototype.initialize = function(color) {
    this.Car_initialize(color);
    ...
};
Truck.prototype.tow = function(weight){};

And this works fine, but how can we do this using just objects and factory methods? The key method you will need is the extend method which we can borrow from jquery $.extend() or any other JavaScript frameworks. The extend method basically copies all the properties and methods from one object to another. You've probably used this to create copies of data objects already.

With this method we can do the same operation as above but with factory methods instead of fake classes. For the Truck object, we can just 'extend' and internal car object using the extend method.
function createCar (color) {
    var _color = color,
        _wheels = 4,
        _condition = 'good';
        
    return {
        getColor : function () { return _color; },
        setColor : function (color) {
            _color = color;
        },
        drive : function (speed) {}
    }
}
//and then extend Car and create a Truck
function createTruck (color) {
    var _car = createCar(color),
        _type = 'pickup';
    
    //here we can use our extend method to add new methods to truck
    return $.extend(_car, {
        getType : function () { return _type; },
        tow : function (weight) {}
    });
}

//now we can create a truck and drive
var myTruck = createTruck(#bada55);
console.log(myTruck.getType());
myTruck.drive(90);


One of the great things about this way is you can now have real private variables. There is no way to access _color and _wheels, unless you create getters and setters which is generally a good practice. We can even have private functions inside our factory methods. Here is a Wiki link to learn more about the Factory Method Pattern. This article only covers a small part of it.

How is this used in Angular? Every time you call a app.controller(), app.directive(), app.factory(), etc. you are supplying a factory method to create this object. Now, thinking this way, I wonder if controllers, directives, or services could be extended? Hmm, not sure, I'll have to do some testing.

No comments:

Post a Comment