I'm wondering if anyone knows why call is so much faster than apply? In chrome, it's roughly 4x faster, and 30x in firefox, and I can even make a custom prototype, apply2, that does (in most cases) run 2x as fast as apply (the idea taken from angular):
Function.prototype.apply2 = function( self, arguments ){
switch( arguments.length ){
case 1: this.call( self, arguments[0] ); break;
case 2: this.call( self, arguments[0], arguments[1] ); break;
case 3: this.call( self, arguments[0], arguments[1], arguments[2] ); break;
case 4: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3] ); break;
case 5: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4] ); break;
case 6: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5] ); break;
case 7: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6] ); break;
case 8: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7] ); break;
case 9: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8] ); break;
case 10: this.call( self, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9] ); break;
default: this.apply( self, arguments ); break;
}
};
So does anyone know why?
Referencing the ECMAScript Language Specification 5.1 Edition (June 2011):
When the apply method is called on an object func with arguments thisArg and argArray, the following steps are taken:
If IsCallable(func) is false, then throw a TypeError exception.
If argArray is null or undefined, then
return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and an empty list of arguments.
Type(argArray) is not Object, then throw a TypeError exception.len be the result of calling the [[Get]] internal method of
argArray with argument "length".n be ToUint32(len).argList be an empty List.index be 0.index < n
indexName be ToString(index).nextArg be the result of calling the [[Get]] internal method of
argArray with indexName as the argument.nextArg as the last element of argList.index to index + 1.[[Call]] internal method of func,
providing thisArg as the this value and argList as the list of
arguments.When the call method is called on an object func with argument thisArg and optional arguments arg1, arg2 etc, the following steps are taken:
IsCallable(func) is false, then throw a TypeError exception.argList be an empty List.arg1 append each argument as the last
element of argList
[[Call]] internal method of func,
providing thisArg as the this value and argList as the list of
arguments.As we can see, the format in which apply is specified is notably heavier and needs to do a lot more due to the need to change the format in which the arguments are given and how they are finally needed.
There are a number of checks in apply which are not necessary in call due to the difference of input formatting.
Another key point is the manner in which arguments are looped over (steps 4-12 in apply, implied in step 3 of call): the whole set-up for looping is executed in apply regardless of how many arguments there actually are, in call all of this is done only if needed.
Additionally it's worthwhile noting that the way in which step 3 in call is implemented isn't specified, which would help explain the drastic differences in different browser behavior.
So to shortly recap: call is faster than apply because the input parameters are already formatted as necessary for the internal method.
Be sure to read the comments below for further discussion.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With