Élargir davantage les réponses précédentes ...
Du point de vue général des compilateurs et sans tenir compte des optimisations spécifiques aux VM:
Tout d'abord, nous passons par la phase d'analyse lexicale où nous tokenisons le code.
A titre d'exemple, les jetons suivants peuvent être produits:
[]: ARRAY_INIT
[1]: ARRAY_INIT (NUMBER)
[1, foo]: ARRAY_INIT (NUMBER, IDENTIFIER)
new Array: NEW, IDENTIFIER
new Array(): NEW, IDENTIFIER, CALL
new Array(5): NEW, IDENTIFIER, CALL (NUMBER)
new Array(5,4): NEW, IDENTIFIER, CALL (NUMBER, NUMBER)
new Array(5, foo): NEW, IDENTIFIER, CALL (NUMBER, IDENTIFIER)
J'espère que cela devrait vous fournir une visualisation suffisante pour que vous puissiez comprendre combien de traitement est nécessaire.
Sur la base des jetons ci-dessus, nous savons que ARRAY_INIT produira toujours un tableau. Nous créons donc simplement un tableau et le peuplons. En ce qui concerne l'ambiguïté, l'étape d'analyse lexicale a déjà distingué ARRAY_INIT d'un accesseur de propriété d'objet (par exemple obj[foo]
) ou de crochets à l'intérieur de chaînes / littéraux regex (par exemple "foo [] bar" ou / [] /)
C'est minuscule, mais nous avons aussi plus de jetons avec new Array
. De plus, il n'est pas encore tout à fait clair que nous souhaitons simplement créer un tableau. On voit le «nouveau» jeton, mais «nouveau» quoi? Nous voyons alors le jeton IDENTIFIER qui signifie que nous voulons un nouveau «tableau», mais les VM JavaScript ne distinguent généralement pas un jeton IDENTIFIER et des jetons pour les «objets globaux natifs». Par conséquent...
Nous devons rechercher la chaîne de portée chaque fois que nous rencontrons un jeton IDENTIFIER. Les VM Javascript contiennent un "objet Activation" pour chaque contexte d'exécution qui peut contenir l'objet "arguments", des variables définies localement, etc. Si nous ne pouvons pas le trouver dans l'objet Activation, nous commençons à rechercher la chaîne de portée jusqu'à ce que nous atteignions la portée globale . Si rien n'est trouvé, nous lançons un fichier ReferenceError
.
Une fois que nous avons localisé la déclaration de variable, nous appelons le constructeur. new Array
est un appel de fonction implicite, et la règle de base est que les appels de fonction sont plus lents pendant l'exécution (d'où la raison pour laquelle les compilateurs statiques C / C ++ autorisent la "fonction en ligne" - ce que les moteurs JS JIT tels que SpiderMonkey doivent faire à la volée)
Le Array
constructeur est surchargé. Le constructeur Array est implémenté en tant que code natif, il fournit donc des améliorations de performances, mais il doit toujours vérifier la longueur des arguments et agir en conséquence. De plus, dans le cas où un seul argument est fourni, nous devons vérifier davantage le type de l'argument. new Array ("foo") produit ["foo"] où comme new Array (1) produit [undefined]
Donc, pour simplifier tout cela: avec les littéraux de tableau, la VM sait que nous voulons un tableau; avec new Array
, la machine virtuelle doit utiliser des cycles CPU supplémentaires pour déterminer ce que new Array
fait réellement .