Techniques Behind Modern Web
6 Jan
You may never use object extension in Javascript just because you, and many others, believe that a prototype based OOP languages like JS are unable to support this classical OOP feature.
Matt Snider has comprehensive articles about object extension in JavaScript that show how JQuery (<= v1.2) and Prototype (<=v1.5) frameworks use “member copy” method, a brutal force technique to iterate through all object members and copy them to other object, in comparison with a better technique: “Prototype Clone”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | 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; }; |
Though member copy method is easy to understand and allows multiple inheritance, it is naturally rather slow. Matt’s preferred method “prototype clone” is much faster.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /** * 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); } }, |
And he suggests a simpler function (which he uses in his projects):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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]; } } }; |
An example of how you might use the extend Function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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; } }); |
And here is improved version that can call the super class from the child class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | Core.Model.XJsonArray = function(data) { this.update(data); }; Core.extend(Core.Model.XJsonArray, Core.Model.Model, { length: 0, … /* More to come in my Tuesday Article */ /** * Updates the object to use the passed data set * * @method update * @param data {object} xjsonarray object * @public */ update: function(data) { if (! (data && isType(data, ‘object’) && isType(data.scheme, ‘array’) && isType(data.set, ‘array’))) {throw(’XJsonArray - Invalid data passed into Update’);} this.parent.update.call(this, data.set); this.length = this.data.length; this.scheme = data.scheme; } }); |
You can read full articles here:
Free Website Magazine: Know more than your competitors with Website Magazine
Don't forget to subscribe
so you don't miss out on future posts!
One Response for "JavaScript Object Extension"
Glad you liked my post. I eventually added reference to super classes.
http://mattsnider.com/javascript/improving-extend-with-super/
Leave a reply