Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Saturday, December 1, 2007

Object Extension

Object extension is one of the most important aspects of an OOP language. Many people believe that a prototype based OOP languages, such as JavaScript, are unable to support this classical OOP feature. Frankly, these people are wrong, and most likely have never studied a prototype based OOP. There are, in fact, two ways (if you know of more, please let me know) of extending objects in JavaScript: Member Copy and Prototype Clone.

Member Copying

This is a brute force technique and the most common method used for extending Objects. Most frameworks started using this technique, see jQuery (<= v1.2) and prototype (<=1.5) extend methods. In this technique you iterate through each member in Object A and add it to Object B. Often Object B is created when the extend Function is executed. Here is an example snippet from jQuery 1.2 extend:

Example 1: Memeber Copy as Implemented By jQuery 1.2

jQuery.extend = jQuery.fn.extend = function() { // copy reference to target object var target = arguments[0] || {}, a = 1, al = arguments.length, deep = false; // Handle a deep copy situation if ( target.constructor == Boolean ) { deep = target; target = arguments[1] || {}; } // extend jQuery itself if only one argument is passed if ( al == 1 ) { target = this; a = 0; } var prop; for ( ; a < al; a++ ) // Only deal with non-null/undefined values if ( (prop = arguments[a]) != null ) // ACTUAL COPY BEGINS HERE // Extend the base object for ( var i in prop ) { // Prevent never-ending loop if ( target == prop[i] ) continue; // Recurse if we’re merging object values if ( deep && typeof prop[i] == ‘object’ && target[i] ) jQuery.extend( target[i], prop[i] ); // Don’t bring in undefined values else if ( prop[i] != undefined ) target[i] = prop[i]; } // ACTUAL COPY ENDS HERE // Return the modified object return target; };

There is a lot going on here, because jQuery allows for deep copy (recursively copying members of the extension objects) and the simultaneous extension of multiple objects. My all-caps “ACTUAL COPY …” comments wrap the actual copy code. The “for in” statement iterates through the extension Object (prop[i]) and applies it to the extending Object (target[i]).

The benefits of the Member Copy method are that it is easily understood and makes multiple inheritance trivial. The negatives are that it is much slower than the Prototype Clone method, it is easy to accidentally override methods, and a bit of hackery is needed to do a deep extension of an Object.

It is slower because you have to iterate through every member of the extension Object and then apply each of those members to the extending Object. In the Prototype Clone method, this can be done in 3 lines of non-looping code. Accidentally overriding methods is a problem, because you might be extending Object A, which has a method ‘toString’ with Object B, which also has a method ‘toString’. However, you wanted to keep the ‘toString’ method of Object A. The Prototype Clone method only allows for single inheritance, which prevents such pit-falls.

Prototype Clone

This is my favored technique as performance is the most important aspect of my applications. The user should never have to wait for your JavaScript to execute and this method can be much faster than the Member Copy method as the operation cost is a constant time operation versus an O(n) operation. Here is an example of the Prototype Clone method from YUI:

Example 2: Prototype Clone as Implemented By YUI 2.3

/** * Utility to set up the prototype, constructor and superclass properties to * support an inheritance strategy that can chain constructors and methods. * Static members will not be inherited. * * @method extend * @static * @param {Function} subc the object to modify * @param {Function} superc the object to inherit * @param {Object} overrides additional properties/methods to add to the * subclass prototype. These will override the * matching items obtained from the superclass * if present. */ extend: function(subc, superc, overrides) { if (! superc || ! subc) { throw new Error(”YAHOO.lang.extend failed, please check that ” + “all dependencies are included.”); } var F = function() {}; F.prototype = superc.prototype; subc.prototype = new F(); subc.prototype.constructor = subc; subc.superclass = superc.prototype; if (superc.prototype.constructor == Object.prototype.constructor) { superc.prototype.constructor = superc; } if (overrides) { for (var i in overrides) { subc.prototype[i] = overrides[i]; } YAHOO.lang._IEEnumFix(subc.prototype, overrides); } },

In this method you first create an anonymous Function ‘F’ and copy the prototype of the superclass to that Function (this prevents changes to the subclass from affecting the superclass). Then instantiate ‘F’ assigning it to the prototype of the subclass. This creates memory pointers on the subclass’ prototype Object to the members attached to the prototype of the superclass. Now when you instantiate the subclass Object, it will have all members of the superclass. It is important to note that you should not be extending an Object that already has values on its prototype, but instead you should use this technique when you first create the subclass Object.

YUI also adds some additional Object pointers (constructor and superclass), which make these Objects behave more like a Classical OOP language. I disagree with this action, because you can extend the Object object, and this method will modify the prototype of Object and break future “for in” statements on all Object objects. They use these methods in detecting Object types and the inheritance model, but I have not yet been convinced of the need for this.

I do like how they allow you to pass in overrides. This is an Object whose members are applied to the subclass after the extension occurs. This allows you to extend and initialize the subclass in one statement.

I suggest a simpler function (which I use in my projects), as follows:

Example 3: Simple Prototype Clone

Core.extend = function(subc, superc, overrides) { if (! superc || ! subc) { throw new Error(”Core.extend failed, please check that all dependencies are included.”); } var F = function() {}; F.prototype = superc.prototype; subc.prototype = new F(); if (overrides) { for (var i in overrides) { subc.prototype[i] = overrides[i]; } } };

And here is an example of how you might use the extend Function:

Example 4: Using Extend

var Item= {}; var Book = {}; Core.extend(Item, Object, { toString: function() { var sb = []; for (var i in this) { if (typeof this[i] !== ‘function’) { sb.push(’&'); sb.push(i); sb.push(’='); sb.push(this[i]); } return sb.join(”); } }); Core.extend(Book, Item, { getTitle: function() { return this.title; }, setTitle: function(s) { this.title = s; } });

If you really need to know the inheritance chain, then I suggest attaching a stack to each new Object that contains the inheritance chain. That way you won’t break ‘for in’ operations of the Object object.

…………………………
Edited: March 15, 2008

I eventually, found a good use for referencing super class, and here is how I modified this method to support it:

Improving Extend With Super

posted by Matt Snider at 3:18 pm  

Friday, September 14, 2007

jQuery 1.2 Released

If you didn’t already know, jQuery is a lightweight JavaScript Framework that introduced powerful DOM querying and Function chaining. Check out their documentation for more information.

The team has added a lot in this version, rounding out the Framework’s rough edges. It is important to note that this version is not backwards compatible with previous versions and you will need to use the supplied plugin for jQuery 1.1.4 backwards compatibility.

jQuery 1.2

posted by Matt Snider at 5:59 pm  

Tuesday, August 21, 2007

jQuery vs. Prototype from Agile Ajax

I saw this post on one of my favorite blogs, Agile Ajax. Despite the title, it is not an analysis of jQuery versus Prototype but a discussion of the OOP design of the two Frameworks. I like Brian’s analogy of Prototype being JavaScript with training wheels and other Frameworks like programming without training wheels. Using Prototype has a tendency to make coders lazy, attaching everything to the ‘this’ operating and using event and function binding all over the place, when it is not really necessary or efficient. The Module Pattern is the most efficient pattern I have used and Brian Dillard discusses this as well at the end of his article. Enjoy:

jQuery vs. Prototype: OO JavaScript with or without training wheels

posted by Matt Snider at 5:16 pm  

Monday, July 2, 2007

jQuery, iFirebug, and iPhone Programming

Some important events happened in the JavaScript community this past week: new jQuery release, Joe Hewitt wrote an iPhone extension for Firebug, and developing for the iPhone is getting a lot of hype. Here are some articles that do a great job covering this:

posted by Matt Snider at 2:31 pm  

Sunday, May 6, 2007

ExtJs

Jack Slocum, author of YAHOO.Ext, has recently launched http://extjs.com/. Initially, I thought the site was a remake of his personal site http://jackslocum.com/ with a greater focus on his YUI extension library. However, upon closer inspection, I realized the new website is dedicated also to framework interoperability. And since that is one of the main goals of this blog, I felt it worth mentioning. Anyway, Ext already works with jQuery, Prototype, and Scriptaculous.

Note: Ext is now also works standalone, without any other library. Although, I am impressed with the work this team has put into Ext, it is still to heavy for most websites. Using this Framework, it is very easy to create JavaScript libraries of 300k or more.

posted by Matt Snider at 10:12 pm  

Powered by WordPress