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:
Sponsored by Free Website Trade Publication >> Website Magazine
Don't forget to subscribe
so you don't miss out on future posts!
One Response for "JavaScript Object Extension"
http://mattsnider.com/javascript/improving-extend-with-super/
Leave a reply