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?
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?
Share Improve this question edited Apr 13, 2022 at 11:58 Dharman♦ 33.4k27 gold badges101 silver badges147 bronze badges asked May 20, 2014 at 20:42 user578895user578895 3- I thought this site provides some interesting statistics jsperf./function-calls-direct-vs-apply-vs-call-vs-bind/6 – malkassem Commented May 20, 2014 at 20:49
- This jsperf also shows call outperforming jsperf./apply-vs-call – Travis J Commented May 20, 2014 at 20:53
- This answer and ment(s) has some discussion about possible reasons why call may be faster than apply: stackoverflow./a/8040293/1253479 – Jack Commented May 20, 2014 at 21:26
1 Answer
Reset to default 16Referencing the ECMAScript Language Specification 5.1 Edition (June 2011):
15.3.4.3 Function.prototype.apply (thisArg, argArray)
When the apply
method is called on an object func with arguments thisArg and argArray, the following steps are taken:
If
IsCallable(func)
isfalse
, then throw aTypeError
exception.If
argArray
isnull
orundefined
, thenreturn
the result of calling the[[Call]]
internal method offunc
, providingthisArg
as thethis
value and an empty list of arguments.- If
Type(argArray)
is notObject
, then throw aTypeError
exception. - Let
len
be the result of calling the[[Get]]
internal method ofargArray
with argument"length"
. - Let
n
beToUint32(len)
. - Let
argList
be an emptyList
. - Let
index
be 0. - Repeat while
index < n
- Let
indexName
beToString(index)
. - Let
nextArg
be the result of calling the[[Get]]
internal method ofargArray
withindexName
as the argument. - Append
nextArg
as the last element ofargList
. - Set
index
toindex + 1
. - Return the result of calling the
[[Call]]
internal method offunc
, providingthisArg
as thethis
value andargList
as the list of arguments.
15.3.4.4 Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )
When the call
method is called on an object func with argument thisArg and optional arguments arg1, arg2 etc, the following steps are taken:
- If
IsCallable(func)
isfalse
, then throw aTypeError
exception. - Let
argList
be an emptyList
. - If this method was called with more than one argument then in left
to right order starting with
arg1
append each argument as the last element ofargList
- Return the result of calling the
[[Call]]
internal method offunc
, providingthisArg
as thethis
value andargList
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 ments below for further discussion.