הלינקייה: מגזין חודשי למפתחים

רוצה לשמוע על כל האירועים, המדריכים, הקורסים והמאמרים שנכתבו החודש ?
הלינקייה הינו מגזין חופשי בעברית שמשאיר אותך בעניינים.
בלי ספאם. בלי שטויות. פעם בחודש אצלך בתיבה.

Object Oriented Javascript

The basic Object Oriented JavaScript has no classes, only objects. An object is a hash containing key-value pairs. Since functions are normal variables in JavaScript, methods are simply values in that hash.

Here's the first critter object in JavaScript.

"use strict";

(function() {
    // In javascript, an object is simply a hash literal. 
    // here's a simple critter

    var critter = {
        name : 'Eek', 
        age  : 0,
    };

    critter.greet = function() {
        console.log('Hello, My name is ' +
                    this.name            +
                    ' and I am '         +
                    this.age             +
                    ' years old');
    };

    // prints "Hello, My name is Eek and I am 0 years old
    critter.greet();
}());

Access Control

To implement Access control in JavaScript, we use closures. When a variable is defined in a block with a function, that function can access the variable, but outside code cannot. JavaScript takes this one step further and keeps the variable alive as long as the function is alive.
Using closures, we can write code like the following:

"use strict";

(function() {
  
  function make_counter(end) {
    var x = 0;
    var counter = function() {
      if ( x < end ) {
        return ++x; 
      }
      else {
        return false;
      }
    }

    return counter;
  }

  var c = make_counter(5);
  var d = make_counter(7);

  // prints 1
  console.log(c());
  // prints 2
  console.log(c());

  // A new counter - prints 1
  console.log(d());

}());

Implementing Access Control features using closures is simple. Just create the variable that needs controlling outside the object hash, and in the hash, create a function that will be able to access that variable lexically.
Here's how a UserInfo object is implemented using this technique:

"use strict";

(function() {

    var UserInfo = function(opts) {
        if ( ( typeof(opts.name) === 'undefined' ) ||
             ( typeof(opts.password) === 'undefined') ) {
            throw 'Missing Required Attribute';
        }

        var iPassword = opts.password,
            that     = {
                username : opts.name
            };

        that.login = function(password) {
            return ( password === iPassword );
        }

        return that;
    }


    var user = UserInfo({name : 'Tom', password : 'waits'});

    console.log(user.login('mmm'));
    console.log(user.login('waits'));

    console.log(user.username);
    console.log(user.password);


}());


Inheritance And Code Reuse

There are many inheritance patterns in JavaScript. In this section, I will demonstrate using the prototype chain to reuse code from other objects.

Every function in JavaScript has a special property called "prototype". When the function is used as an object, and we call a method on it that is not defined in the object, JavaScript will try to locate this method in the object referred to by the prototype property. This allows us to create prototype chains (much like inheritance chains in class based languages).

Here's an example for using prototypes in JavaScript:

"use strict";

(function(global) {

    global.utils = global.utils || {};
    global.utils.object = function(o) {
        function F() {};
        F.prototype = o || Object;
        var nf = new F();
        return new F();
    };

    var object = global.utils.object;

    var f = {};
    f.foo = function() { console.log("foo") };

    var b = object(f);
    b.foo();

}(exports));


(function(global) {
    var object = global.utils.object;

    var Person = function(name) {
        var that = object();
        that.name = name;

        that.prototype.greet = function(who) {
            if ( typeof(who) === "undefined" ) {
                console.log("[ " + that.name + " ] Hello");
            } else {
                console.log("[ " + that.name + " ] Hello, " + who);
            }

        };

        return that;
    };

    var Student = function(name, year) {
        var that = object(Person(name));

        that.year = year;

        that.prototype.is_graduate = function() {
            return that.year >= 3;
        };

        return that;
    };

    var john = Person("John");
    var tim  = Person("Tim");

    var jane = Student("Jane", 2);
    var jude = Student("Jude", 3);

    john.greet();
    jane.greet();

    console.log(jude.is_graduate());


    var NiceStudent = function(name, year) {
        var that = object(Student(name, year));

        that.greet = function(who) {
            Student.prototype.greet(who);
            console.log("  And I am looking for a job");
        };

        return that;
    };

    var brad = NiceStudent("Brad", 3);
    brad.greet();

}(exports));


The Singleton Pattern

Since there are no classes in JavaScript, A singleton (which, in class based OO is a class with no callable new method), is simply an object.
If you only build one instead of providing a way to build many, the pattern is ready.

"use strict";

(function(global) {
   
    var log = {
        debug : console.log,
        warn  : console.log
    };

    global.log = log;

    // now we can use the log property of the global object to reach our log object.
}(exports));


The Factory Pattern

A factory is an object capable of creating other objects. It takes a name of a product and returns a new instance of that product.
JavaScript makes it easy to write Factories, by storing constructor functions as members of the factory object.
Here's the implementation using cars:

"use strict";

(function() {
    var object = function(o) {
        function F() {};
        F.prototype = o || Object;
        return new F();
    };

    var CarFactory = {};

    // a base object for all car products.
    var car = object();
    car.constructor.prototype.drive = function() {
        console.log("Vroom .... I have " + this.doors + " doors");
    };

    CarFactory.Compact = function() {
        var that = object(car);
        that.doors = 4;
        
        return that;
    };

    CarFactory.Convertible = function() {
        var that = object(car);
        that.doors = 2;
        return that;
    };

    CarFactory.SUV = function() {
        var that = object(car);
        that.doors = 7;
        return that;
    };

    CarFactory.produce = function(model) {
        var ctor = CarFactory[model];
        return ctor();
    };

    var corolla = CarFactory.produce('Compact');
    var solstice = CarFactory.produce('Convertible');
    var cherokee = CarFactory.produce('SUV');

    corolla.drive();
    solstice.drive();
    cherokee.drive();
}());