Monday, November 3, 2014

Using objects for switch statements

I ran across a post where the author wished switch statements returned a value. The switch statement has always looked to me like a poor choice in programming. It seems like we should be able to use polymorphism instead of what is another version of a long if/else chain. However, there are cases when we could be over-designing and a switch statement would do the job.
// Not valid ECMAScript
var color = switch(x) {
  case   1: 
    'blue' ; break;
  case   2: 
    'red'  ; break;
  default : 
    'green';
};

The author, Dusan Jovanovich, proposed a cond function in the likes of lisp
cond( x, 
      1, 'blue', 
      2, 'red', 
      'green' );
Since lisp is the language where non-primitives are lists, and JavaScript is the language where non-primitives are basically maps, I would suggest a cond function that feels more like JavaScript.
function cond(val, cases, fallback, scope) {
    var theCase = cases.hasOwnProperty(val) ? cases[val] : fallback;
    return typeof theCase == "function" ? 
        theCase.call(scope || this, val) : 
        theCase; 
}
So that the previous cond call would look like:
var color = cond(x , {
   1: 'blue',
   2: 'red',
}, 'green');
One of the benefits of this function is that you can pass functions to each condition, so that we get almost all functionality of a switch:
jQuery('a').each(function(){
 switch(x) {
  case 1: 
    $(this).css({ color: 'blue'});
    $this.slideDown();
    break ;
  case 2: 
    $(this).css({ color: 'red'});
    $this.slideUp();
    break ;
  default : 
    $(this).css({ color: 'green'});
 }
});

// Could become
jQuery('a').each(function(){
  cond(x, {
    1: function() {
        $(this).css({ color:'blue'});
        $(this).slideDown();
    },
    2: function() {
        $(this).css({ color:'red'});
        $(this).slideUp();
    },
  }, function() {
        $(this).css({ color:'green'});
  }, this);
});
If you have multiple cases where the almost the same thing needs to happen (fallthroughs), the switched value is passed to each of the conditions, so you could nest them. At this point, this probably would not be a good idea, but here it goes anyway.
jQuery('a').each(function(){
  // Save 'this' instead of passing in a scope in this case
  var $this = $(this);
  
  function fallThrough12(y) {       
      cond(y, {
           1: function() {
              $this.css({ color:'blue'});
           }
           2: function() {
              $this.css({ color:'red'});
           }
      });
      $this.slideDown();
  }
  cond(x, {
    1: fallThrough12,
    2: fallThrough12,
    3: function() {
        $this.css({ color:'yellow'});
        $this.fadeOut();
    }
  }, function() {
        $this.css({ color:'green'});
  });

Monday, October 6, 2014

Understanding prototypal inheritance

Many developers coming from a classical inheritance model find it difficult to grasp JavaScript's prototypal inheritance. In this post, I will explain how inheritance is achieved in JavaScript and what pitfalls to avoid.

Consider the following code where we create an instance of "Class" using code that looks like classical inheritance
function Class() {
    this.obj2 = {c: 'c'}
}

Class.prototype = {
    primitive: 'a',
    obj: { a: 'a' },
    arr: [1,2,3]
};

var inst1 = new Class();
var inst2 = new Class();

The following diagram explains which objects are being created. Solid lines are direct links and dashed lines are chaining objects through the prototype.


An equivalent way of creating an instance, in a more prototypal, albeit more verbose fashion is the following:
// Create an empty object chaining it to Class.prototype
var inst1 = Object.create(Class.prototype);
// Initialize the object using the constructor
Class.call(inst1);

The important thing to understand is that the prototype object is shared by all instances. A common pitfall is to instantiate an object/array on the prototype without realizing that mutating that object will modify it for all instances. In our example, the {a: 'a'} object and the [1,2,3] array is shared among the instances, whereas each instance has its own copy of {c: 'c'}
// The two instances share the same instance of obj
// which may be unexpected
inst1.obj.a = 'b';
console.log(inst1.obj); // {a: 'b'}
console.log(inst2.obj); // {a: 'b'}

// same for arrays (since array is a mutable object)
inst1.arr.push(4);
console.log(inst1.arr); // [1,2,3,4]
console.log(inst2.arr); // [1,2,3,4]

// to get around this, create the child objects in constructor
// now changing obj2 in instance1 does not affect instance2
inst1.obj2.c = 'd';
console.log(inst1.obj2); // {c: 'd'}
console.log(inst2.obj2); // {c: 'c'}
Note that this problem doesn't occur when sharing primitives on the prototype. When we read a property from an object, it will first look for the property in the specified object, if it doesn't find it, it will go up the prototype chain looking for that object. However, when you write to an object, it always writes to the specified object, not the prototype. For example

// Here we creating a new property on inst1, which now shadows 
// the "primitive" property on "Class.prototype"
inst1.primitive = 'b';
// Still reading from Class.prototype
console.log(inst2.primitive); // 'a'
// Read "obj" from the prototype and then write over the "a" 
// property on that "obj" (from the prototype)
inst1.obj.c = 5;
// Create a new object that writes a new "obj" property on inst2 
// directly and shadows "obj" on the prototype
inst2.obj =  {c: 6};

Lessons to learn:

  • Initializing immutable objects on a prototype keeps a single copy, so it saves memory and initialization code
  • Don't share mutable objects on the prototype unless you want changes in one instance to reflect in all instances.
  • Initializing primitives on the prototypes is always OK, because they can't be mutated directly through instances
 If you'd like to play around with the above code, go to http://jsfiddle.net/mendesjuan/cdaq5b11/3/