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