Too Much Recursion From Extending Objects

The other day I spent a couple hours beating my head, wondering why I was getting too much recursion in my objects. My project had 3 objects, where object C extended object B, and B extended object A. Each Object had a Function update, and the first statement inside update was to call the parent objects update Function.

Example 1: Call to Function in Parent Object

.update(node) {
	this.parent.update.call(this, node);
	…
},

I have been using "Core.extend" for a while and referencing the parent Object, but I had never tried to extend a child Object, while Function chain to the super class of the super class. In simpler terms, While I have often created an Object B extending an Object A, with a Function (such as update) that called the same Function in the parent Object A. I had never then also created an Object C extending Object B, which called a Function in B that then called a Function in A. For example:

Example 2: C Extends B Extends A

var A = function() {};
var B = function() {};
var C = function() {};

A.prototype = {
	update: function(obj) {
		// some logic
	}
};

B.prototype = {
	update: function(obj) {
		this.parent.update.call(this, obj);
		// some logic
	}
};

C.prototype = {
	update: function(obj) {
		this.parent.update.call(this, obj);
		// some logic
	}
};

Core.extend(B, A);
Core.extend(C, B);

Initially, this looks correct, as it is the same pattern that one always uses when extending and calling a Function on the parent Object. However, in this example, if you call the update Function of C, you will get a "Too Much Recursion" error, or simply crash your browser. Why?

Well, when you call the parent Function, you generally pass in the scope of the current Object, that way any operations in the parent Function are applied to the current Object and not the prototype of the parent Object. However, if Object C passes its scope to Object B, then when the update Function of Object B, calls its parent, it is actually calling the parent of Object C (since we are executing B in the scope of Object C). This then, creates an infinite loop.

I spent some time thinking and searching the web, but could not find an elegant way to change the extend Function to handle these situations. In the end, I had to hack the way Object C calls its parent Object B, in order prevent the loop. Here is what I did:

Example 3: Fixing the Infinite Loop

var A = function() {};
var B = function() {};
var C = function() {};

A.prototype = {
	update: function(obj) {
		// some logic
	}
};

B.prototype = {
	update: function(obj, scope) {
		var that = scope || this;
		this.parent.update.call(that, obj);
		// some logic; use variable that instead of this to reference self
	}
};

C.prototype = {
	update: function(obj) {
		this.parent.update.call(this.parent, obj, this);
		// some logic
	}
};

Core.extend(B, A);
Core.extend(C, B);

In these examples, each of the update Functions expect a parameter obj. I have included this to illustrate that parameters can easily be passed into the Function chain. In my examples I have included only 1 parameter, but you could set the Functions up to accept no parameters or as many parameters as you like. However, to fix the scoping issue of Object B, the last parameter must be an optional scope variable.

With this technique, if you are using a non-extended instance of Object B, you can simply call the update Function with the desired parameters and it works fine. Yet when you call the update Function of Object B from its child (Object C), then the last parameter scope is passed and used as the operational scope inside the update Function of Object B. Inside Object B, you will need to use the that variable every time you mean this, except when calling the update Function of its parent (Object A), as that holds the true (operational) scope of the Function. Object C calls the update Function of Object B with the execution scope of its parent (Object B). So the update Function of Object B executes with the scope of Object B (allow access to the parent of Object B), while using the operational scope of Object C, stored in that. In this way, Object B can call its parent Object A, but we can manipulate the instance of Object C throughout the Function chain.