Room51 Technical Documentation

JavaScript Anonymous Functions

In an object

The object test1 has a say property that points to the defined anonymous function. The object test2 also has a say property that is set to point to the same anonymous function. Emptying test1 has no effect on test2; its say property still points to the same function.


var test1 = {
    say : function(msg) {alert(msg);}
};
test1.say("One");

// Create a new object
// Point its say property to the same function as test1.say
var test2 = {say : test1.say};
test2.say("Two");

// Empty test1
test1 = {};

// test2.say still maintains its reference to the anonymous function
test2.say("Three");

Recursion

The anonymous function explicitly calls test1.countDown until n reaches zero.


var test1 = {
   countDown : function(n) {
      if (n > 0) {
         alert(n);
         test1.countDown(n - 1);
      }
   }
};
	
test1.countDown(3);

Because of the explicit reference to test1.countDown, if the function is attached to another object and then test1 is emptied, then the function will break as test1.countDown will be undefined.


var test1 = {
   countDown : function(n) {
      if (n > 0) {
         alert(n);
         test1.countDown(n - 1);
      }
   }
};
	
test1.countDown(3); // 3, 2, 1

var test2 = {countDown : test1.countDown}
test2.countDown(3); // 3, 2, 1

test1 = {};
test2.countDown(3); // 3, error!

Solutions

Replacing test1.countDown with this.countDown removes the explicit reference. We need to be careful not to 'break' this if using nested functions or timers.


var test1 = {
   countDown : function(n) {
      if (n > 0) {
         alert(n);
         this.countDown(n - 1);
      }
   }
};
	
test1.countDown(3); // 3, 2, 1

var test2 = {countDown : test1.countDown}
test2.countDown(3); // 3, 2, 1

test1 = {};
test2.countDown(3); // 3, 2, 1

An alternative to using this is to label the anonymous function (named anonymous functions!) so it can refer to itself in the function definition. The same name as the object property is used as it serves the same purpose.


var test1 = {
   countDown : function countDown(n) {
      if (n > 0) {
         alert(n);
         countDown(n - 1);
      }
   }
};
	
test1.countDown(3); // 3, 2, 1

var test2 = {countDown : test1.countDown}
test2.countDown(3); // 3, 2, 1

test1 = {};
test2.countDown(3); // 3, 2, 1

This solution avoids the use of this and doesn't require an extra label. Inside any function, arguments.callee refers to the function itself.


var test1 = {
   countDown : function(n) {
      if (n > 0) {
         alert(n);
         arguments.callee(n - 1);
      }
   }
};
	
test1.countDown(3); // 3, 2, 1

var test2 = {countDown : test1.countDown}
test2.countDown(3); // 3, 2, 1

test1 = {};
test2.countDown(3); // 3, 2, 1