There are actually three ways to call a function in AS3. Can you name all three? Do you know which is fastest? Does the type of function being called matter? Today I’ll tackle these questions and find some surprising facts about the three ways to call a function in AS3.

The three ways to call a function are:

  • The () operator—the familiar approach to function calling: foo()
  • The call method—a way to change the this argument: foo.call(thisObj, arg1, arg2, ...)
  • The apply method—a way to change the this argument and pass an Array of parameters: foo.apply(thisObj, argArray)

The call and apply methods provide more advanced function calling features than the () operator. While it’s quite rare to need to change the this argument with call, it is actually rather common to pass an Array of parameters with apply. Also, I should mention that passing the this argument is optional and it is perfectly fine to pass null to get the default behavior. Unfortunately, this isn’t mentioned in the documentation for either call or apply.

With that background in mind, take a look at the following performance test:

package
{
	import flash.display.*;
	import flash.utils.*;
	import flash.text.*;
 
	public class CallingFunctions extends Sprite
	{
		private var __logger:TextField = new TextField();
		private function log(msg:*): void { __logger.appendText(msg + "\n"); }
 
		public function CallingFunctions()
		{
			__logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(__logger);
 
			const REPS:int = 10000000;
			var i:int;
			var beforeTime:int;
			var afterTime:int;
			var functionObject:Function = function(): void {};
			var emptyArray:Array = [];
 
			log("Function Object");
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				functionObject();
			}
			afterTime = getTimer();
			log("\t(): " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				functionObject.call(null);
			}
			afterTime = getTimer();
			log("\tcall(null): " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				functionObject.call(functionObject);
			}
			afterTime = getTimer();
			log("\tcall(functionObject): " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				functionObject.apply(null, emptyArray);
			}
			afterTime = getTimer();
			log("\tapply(null): " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				functionObject.apply(functionObject, emptyArray);
			}
			afterTime = getTimer();
			log("\tapply(functionObject): " + (afterTime-beforeTime));
 
			log("Method");
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				method();
			}
			afterTime = getTimer();
			log("\t(): " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				method.call(null);
			}
			afterTime = getTimer();
			log("\tcall(null): " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				method.call(method);
			}
			afterTime = getTimer();
			log("\tcall(method): " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				method.apply(null, emptyArray);
			}
			afterTime = getTimer();
			log("\tapply(null): " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				method.apply(method, emptyArray);
			}
			afterTime = getTimer();
			log("\tapply(method): " + (afterTime-beforeTime));
 
			log("Static Method");
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				staticMethod();
			}
			afterTime = getTimer();
			log("\t(): " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				staticMethod.call(null);
			}
			afterTime = getTimer();
			log("\tcall(null): " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				staticMethod.call(staticMethod);
			}
			afterTime = getTimer();
			log("\tcall(method): " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				staticMethod.apply(null, emptyArray);
			}
			afterTime = getTimer();
			log("\tapply(null): " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				staticMethod.apply(staticMethod, emptyArray);
			}
			afterTime = getTimer();
			log("\tapply(staticMethod): " + (afterTime-beforeTime));
		}
 
		private function method(): void {}
		private static function staticMethod(): void {}
	}
}

The above test performs many function calls using all three methods on a local Function object, a method of the class, and a static method of the class. It also passes null and the actual this object to call and apply. Here are the results I get on a 2.4 Ghz Intel Core i5 with Mac OS X 10.6 using Flash Player 10.1.102.64: (bold results are fastest for each type of function)

Approach Function Object Method Static Method
() 302 71 79
call(null) 264 799 821
call(this) 358 1213 1224
apply(null) 466 893 882
apply(this) 453 1289 1279

Here are some conclusions we can draw based on the above results table:

  • The normal () operator is by far the fastest way you can call a method, static or otherwise
  • Function objects can actually be called marginally faster with call than ()
  • Using call or apply with methods slows them down so much that they are no longer 4x faster than Function objects but rather 2-4x slower than Function objects
  • Using call and apply with Function objects doesn’t affect the calling speed much, either positively or negatively
  • Passing null to call is always significantly faster than passing the actual this object
  • Passing null to apply is significantly faster only than passing the actual this object with methods. It’s actually slightly slower with Function objects

If any of the above points apply to any performance-critical code you have, you might want to revisit those areas. This is especially true if you’re using call or apply with methods; I suggest you find a way to convert those function calls to the good old () operator!