Pourquoi l'appel est-il beaucoup plus rapide que l'application?

Je me demande si quelqu'un sait pourquoi l' call est tellement plus rapide que l' apply ? Dans Chrome, il est approximativement 4 fois plus rapide, et 30x dans firefox, et je peux même créer un prototype personnalisé, apply2 , qui fait (dans la plupart des cas) exécuter 2 fois aussi rapidement que possible (l'idée tirée d'angulaire):

 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; } }; 

Est-ce que quelqu'un sait pourquoi?

Référencement de l' édition ECMAScript Language Specification 5.1 (juin 2011) :

15.3.4.3 Function.prototype.apply (thisArg, argArray)

Lorsque la méthode d' apply est appelée sur un objet func avec arguments thisArg et argArray, les étapes suivantes sont prises:

  1. Si IsCallable(func) est false , lancez une exception TypeError .

  2. Si argArray est null ou undefined , return le résultat de l'appel de la méthode interne [[Call]] de func , fournissant thisArg comme this valeur et une liste d'arguments vide.

  3. Si Type(argArray) n'est pas Object , lancez une exception TypeError .
  4. Laissez len être le résultat de l'appel de la méthode interne [[Get]] de argArray avec l'argument "length" .
  5. Soit n ToUint32(len) .
  6. Laissez argList être une List vide.
  7. Laissez l' index être 0.
  8. Répétez l'affichage pendant l' index < n
  9. Laissez indexName être ToString(index) .
  10. Laissez nextArg être le résultat de la méthode interne [[Get]] de argArray avec indexName comme argument.
  11. Ajoutez nextArg comme dernier élément de argList .
  12. Définissez l' index sur l' index + 1 .
  13. Renvoie le résultat d'appeler la méthode interne [[Call]] de func , fournissant thisArg comme la valeur et argList comme la liste des arguments.

15.3.4.4 Function.prototype.call (thisArg [, arg1 [, arg2, …]])

Lorsque la méthode d' call est appelée sur un objet func avec argument thisArg et arguments facultatifs arg1, arg2 etc., les étapes suivantes sont prises:

  1. Si IsCallable(func) est false , lancez une exception TypeError .
  2. Laissez argList être une List vide.
  3. Si cette méthode a été appelée avec plus d'un argument puis dans l'ordre de gauche à droite commençant par arg1 ajoutez chaque argument comme dernier élément de argList
  4. Renvoie le résultat d'appeler la méthode interne [[Call]] de func , fournissant thisArg comme la valeur et argList comme la liste des arguments.

Comme nous pouvons le voir, le format dans lequel apply est spécifié est particulièrement plus lourd et doit faire beaucoup plus en raison de la nécessité de changer le format dans lequel les arguments sont donnés et comment ils sont finalement nécessaires.
Un certain nombre de contrôles apply ce qui n'est pas nécessaire dans l' call raison de la différence de formatage des entrées.

Un autre point clé est la manière dont les arguments sont bouclés (les étapes 4-12 dans apply , impliquées à l'étape 3 de l' call ): l'ensemble de la mise en boucle est exécuté en apply quel que soit le nombre d'arguments existants, en call Tout cela ne se fait que si nécessaire.
En outre, il est intéressant de noter que la façon dont l'étape 3 de l' call est implémentée n'est pas spécifiée, ce qui aiderait à expliquer les différences drastiques dans les différents comportements du navigateur.

Pour rappeler à bref délai: l' call est plus rapide que l' apply car les paramètres d'entrée sont déjà formés au besoin pour la méthode interne.

Assurez-vous de lire les commentaires ci-dessous pour une discussion plus approfondie.