JavaScript – Simplest Inheritance Before ES5

One of the great things about reading through Douglas Crockford’s site is that you get to learn about how certain things like prototypal inheritance was implemented prior to ECMAScript 5. Since then Object.create() made its way into JavaScript and has been available in most modern browsers for a while. If you haven’t read through Crockford’s site or seen how prototypal inheritance was done before, here is a quick overview:

You can click here to test out the code yourself. As you may have noticed, the key to prototypal inheritance is as follows (please ignore the names of these prototypes disguising themselves as classes :smile:):

SubClass.prototype = new Class();

Unfortunately, at times you may not be able to safely execute the constructor of the prototype which is being inherited (which in this case would be Class). In order to get around it, you can create a surrogate function whose prototype will become that of the prototype to be inherited:

function SurrogateClass(){}
SurrogateClass.prototype = Class.prototype;
SubClass.prototype = new SurrogateClass();

Unfortunately, it isn’t the most memorable sequence so let’s turn this into a simple function:

function inherit(Class, SubClass) {
    function SurrogateClass(){}
    SurrogateClass.prototype = Class.prototype;
    SubClass.prototype = new SurrogateClass();
}

Finally, let’s minify it:

function inherit(C,S,p){function O(){}O[p='prototype']=C[p];S[p]=new O}

OK, so this is minification to an extreme since I placed p in the function’s signature. I also went a bit far by sketchily defining p in the left-hand side of the assignment of the prototype to the surrogate function. Even though the above code seems to work, just to be safe, here is a slightly longer, more trustworthy definition:

In the end, both of these definitions are extremely small so I’d say even prior to ES5, prototypal inheritance wasn’t as difficult as one might have thought. 😎

JavaScript – Creating Classes

As many people know, JavaScript doesn’t really have classes but you can mimic some of their behavior with the prototypal setup available in JavaScript. Still, a lot of times it is just easier when you have a function that does most of the work for you. For that reason, I wrote the classify:

/**
 * @license JS Classify (v1.1) - By Chris West - MIT License
 */
/**
 * Creates a class using the specified constructor and options.
 * @param  {!Function} constructor  The constructor function.
 * @param  {{ privateKey:string, setters:Array, getters:Array<=string>, prototype:Object, properties:Object, superClass:Function }} options
 *     Object containing the class options.  The privateKey is the name of the
 *     privateKey that will be assigned to every instance.  The setters array
 *     contains names of private data members for which setters will be setup.
 *     The getters array contains names of private data members for which
 *     getters will be setup.  The prototype object will contain the prototype
 *     values that will be attached to the class' prototype.  The superClass
 *     is the function that will act as the class' super class and all
 *     prototypes will be inherited from it.
 * @return {!Function}  The newly created class.
 */
var classify = (function(fakeClass) {
  var hasOwnProperty = {}.hasOwnProperty;
 
  function camelCase(str, delim) {
    delim = delim || ' ';
    var pos;
    while ((pos = str.indexOf(delim)) + 1) {
      str = str.slice(0, pos) + str.charAt(pos + 1).toUpperCase() + str.slice(pos + 2);
    }
    return str;
  }
 
  /**
   * Setup inheritance.
   * @param {!Function} baseClass  The base class from which the subclass
   *     inherits its prototypal values.
   * @param {!Function} subClass  Class which inherits from the base class.
   * @return {!Function}  The updated subclass.
   */
  function inherit(baseClass, subClass) {
    fakeClass.prototype = baseClass.prototype;
    var prototype = subClass.prototype = new fakeClass();
    prototype.superClass = baseClass;
    return prototype.constructor = subClass;
  }
 
  // Return classify function.
  return function(constructor, options) {
    var outerPrivateData;
 
    var privateKey = options.privateKey || '_';
 
    var realConstructor = function() {
      this[privateKey] = properties && hasOwnProperty.call(properties, privateKey)
        ? properties[privateKey]
        : {};
      if (superClass) {
        this.superClass = superClass;
      }
      try {
        return constructor.apply(this, arguments);
      }
      finally {
        if (superClass) {
          delete this.superClass;
        }
      }
    };
 
    // If the super-class is defined use it.
    var superClass = options.superClass;
    if (superClass) {
      realConstructor = inherit(superClass, realConstructor);
    }
 
    // Add class level properties.
    var properties = options.properties;
    if (properties) {
      for (var key in properties) {
        realConstructor[key] = properties[key];
      }
    }
 
    var realPrototype = realConstructor.prototype;
    var myPrototype = options.prototype || {};
 
    // Add getters.
    var getters = options.getters || [];
    for (var i = 0, len = getters.length; i < len; i++) {
      (function(name) {
        myPrototype[camelCase('get_' + name, '_')] = function() {
          return this[privateKey][name];
        };
      })(getters[i]);
    }
 
    // Add setters.
    var setters = options.setters || [];
    for (var i = 0, len = setters.length; i < len; i++) {
      (function(name) {
        myPrototype[camelCase('set_' + name, '_')] = function(newValue) {
          var privateData = this[privateKey];
          var oldValue = privateData[name];
          privateData[name] = newValue;
          return oldValue;
        };
      })(setters[i]);
    }
 
    // Add all prototypal values.
    for (var key in myPrototype) {
      realPrototype[key] = myPrototype[key];
    }
 
    return realConstructor;
  }
})(function(){});

How To Use classify()

The first parameter that you pass should be the constructor. The second parameter will be an object containing properties representing any options you want to add to the class:

  • privateKey - string
    Defaults to "_". The property name for the object which will house all of the private data for each class instance.
  • getters - Array
    An array of strings indicating the getters that should be automatically setup to retrieve the private data members with the same name. These names will be camel-cased based on underscore characters.
  • setters - Array
    An array of strings indicating the setters that should be automatically setup to set the private data members with the same name. These names will be camel-cased based on underscore characters. All setters return the previous value.
  • properties - Object
    An object containing all of the properties that will be added to the class object.
  • prototype - Object
    An object containing all of the values that should be added to the prototype of the class.
  • superClass - Function
    The super-class from which this new class will inherit. This will overwrite the superClass property of the class' prototype.

Example Classes

The following exemplifies how easy it is to create classes with classify():

// Create a simple base class.
Being = classify(
  function (species) {
    this._.species = species;
  },
  { getters: ['species'] }
);

// Create a more complex sub-class.
Human = classify(
  function (firstName, lastName) {
    this.superClass.call(this, 'Human');
    this._.firstName = firstName;
    this._.lastName = lastName;
  },
  {
    getters: [ 'firstName', 'lastName' ],
    setters: [ 'firstName' ],
    prototype: {
      toString: function() {
        return this.getFullName() + ' (' + this._.species + ')';
      },
      getFullName: function() {
        return this._.firstName + ' ' + this._.lastName;
      }
    },
    superClass: Being
  }
);

The first class that I created above is a Being class which just has one private member: species. The Being.prototype.getSpecies() function is defined for this class as well. The second class is the Human class which is a sub-class of the Being class. The species defaults to "human" while two more private members are included: firstName and lastName. There are getters for all of the private members but there is only one setter which is for the firstName. Additionally the Human.prototype.toString() and Human.prototype.getFullName() functions have been defined to provide additional features to the class.

Try Me!

The above example shows (as long as you are using a modern browser) how these newly created functions work. There is more you can do with this of course so if you want go ahead and play with the above example or copy the classify code and create your own classes. Happy coding! 😎

JavaScript – String.prototype.toProperCase()

Now Available in YourJS

At times you may need to format a string so that it is more appealing. One function that does this is String#toProperCase() because it actually capitalizes the first character of each word. Even though I already had a simple version of this defined in jPaq v1 I thought it would be nice to add the option to lower-case the remaining characters. Here is the updated version of the function definition:

String.prototype.toProperCase = function(opt_lowerCaseTheRest) {
  return (opt_lowerCaseTheRest ? this.toLowerCase() : this)
    .replace(/(^|[\s\xA0])[^\s\xA0]/g, function(s){ return s.toUpperCase(); });
};

Examples of using this function are as follows:

var msg = "Where in the WORLD is Carmen Sandiego?";
alert(msg.toProperCase());  // Where In The WORLD Is Carmen Sandiego?
alert(msg.toProperCase(true));  // Where In The World Is Carmen Sandiego?

At this point this change is scheduled to appear in the next version of jPaq but seeing as how I still don’t know when I will have time to release it I figured it would be a good idea to publicly release it here for everyone to use. Have fun. 😎