Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is call so much faster than apply?

Tags:

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?

like image 866
Nobody Avatar asked May 20 '14 20:05

Nobody


1 Answers

Referencing 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:

  1. If IsCallable(func) is false, then throw a TypeError exception.

  2. 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.

  3. If Type(argArray) is not Object, then throw a TypeError exception.
  4. Let len be the result of calling the [[Get]] internal method of argArray with argument "length".
  5. Let n be ToUint32(len).
  6. Let argList be an empty List.
  7. Let index be 0.
  8. Repeat while index < n
  9. Let indexName be ToString(index).
  10. Let nextArg be the result of calling the [[Get]] internal method of argArray with indexName as the argument.
  11. Append nextArg as the last element of argList.
  12. Set index to index + 1.
  13. Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and argList 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:

  1. If IsCallable(func) is false, then throw a TypeError exception.
  2. Let argList be an empty List.
  3. 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 of argList
  4. Return the result of calling the [[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.

like image 191
Etheryte Avatar answered Sep 18 '22 06:09

Etheryte