Real World JavaScript – Part 2

Javascript’s class design pattern

In javascript, the basic concept of class is implemented through combining a constructor function and an associated prototype object. In BackboneJS, Model represents a chunk of data and bunch of methods that perform computations and transformations on the data. This Model object in BackboneJS implements the class pattern of JavaScript. Let’s dissect it.

var Model = Backbone.Model = function(attributes, options) {
    // Code omitted for brevity
};

This constructor function of BackboneJS Model accepts attributes(the chunk of data mentioned previously) and options.

var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit'];

_.each(modelMethods, function(method) {
    Model.prototype[method] = function() {
      var args = slice.call(arguments);
      args.unshift(this.attributes);
      return _[method].apply(_, args);
    };
});

BackboneJS then attaches a bunch of Underscore utility methods on the Model prototype. Model function in this case comes with a prototype property and BackboneJS has added a bunch of Underscore utility methods on it. When a model instance is created, its prototype will be set to the prototype of Model function. The construnctor is a function and hence it has got prototype property but the instance of Model is an object and it will not have a prototype property. We can access the prototype property of an object o through the use of Object.getPrototypeOf(o) method.

Use hasOwnProperty to check the property inclusion on object / dictionary

UnderscoreJS has got _.has utility to check whether an object (given as first argument) has the specified property (or key given as second argument to _.has) directly on itself. Javascript has in syntax that checks inclusion of property in an object but it takes in to account the inheritance chain, so even a plain literal object is polluted with properties from object prototype.

var o = {};
"toString" in o; // Outputs true in console.

Luckily, object prototype has another method called hasOwnProperty that verifies whether the property is defined directly on the object pr not. But using it directly is susceptible to accidental overwriting of that method.

o.hasOwnProperty("toString"); // outputs false
o.hasOwnProperty = 42; // Overwrites the hasOwnProperty
o.hasOwnProperty("toString") // Error!

In order to avoid the overwriting problem, UnderscoreJS resorts to depending the hasOwnProperty method on the protototype of object as in the following code.

var ObjProto = Object.prototype; 
var hasOwnProperty   = ObjProto.hasOwnProperty; // reference to hasOwnProperty on object prototype

_.has = function(obj, key) {
    return hasOwnProperty.call(obj, key); // Previously stored reference method on prototype is used
 };

Let’s verify all is well.

var o = {};
_.has(o, "toString"); // Outputs false
Object.prototype.hasOwnProperty = 42; // Overwrite hasOwnProperty on object 
_.has(o, "toString") // Outputs false - _.has still works

Using truthiness testing to provide default values

_.memoize = function(func, hasher) {
    var memo = {};
    hasher || (hasher = _.identity);
    return function() {
      var key = hasher.apply(this, arguments);
      return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
    };
};

In UnderscoreJS memoize function, hasher parameter is optional and if it is not provided or falsy values like 0, NaN, null etc are provided, the function will use the identity method as hasher. This tactic is not useful if the falsy values like 0 is acceptable in the function, like in setting the height or width of a div element.

Using double negation to convert to boolean value

Modernizer uses double negation to report and detect various features. Double negation converts the value to its boolean equivalent. If the following code is run in a browser that has openDatabase property in window then it will store the value of true.

tests['websqldatabase'] = function() {
    return !!window.openDatabase;
};

Using slice to clone an array

Slice, if not passed with any arguments, could be used to clone an entire array. That’s what is exactly done in UnderscoreJS’ clone method. Interstingly _.extend is used as the “slice” for an object in the same code.

_.clone = function(obj) {
  if (!_.isObject(obj)) return obj;
  return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
Javscript, the scripting language created in 10 days, has got a bag of tips, quirks, and patterns. This is part two of a series of articles on real world JavaScript usage. - Suhair
comments powered by Disqus