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”.

Member Copy as Implemented By jQuery 1.2

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 &lt; 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 &amp;&amp; typeof prop[i] == 'object' &amp;&amp; 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.

Prototype Clone as Implemented By YUI 2.3

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):

Simple Prototype Clone

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(&amp;');
				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 &amp;&amp; isType(data, ‘object’) &amp;&amp; isType(data.scheme, ‘array’) &amp;&amp; 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!


Related Posts