Tout d'abord, contrairement à d'autres langages, l'efficacité de JS dépend en grande partie de l'efficacité du moteur JS. En plus des avantages et des inconvénients de la mise en œuvre du moteur, le moteur lui-même adoptera également des stratégies d'optimisation pour certains modèles de code spéciaux. Par exemple, les moteurs JS de FF, Opera et Safari ont spécialement optimisé l'opération de concaténation de chaînes (+). Évidemment, pour obtenir une efficacité maximale, vous devez comprendre le tempérament du moteur et essayer de répondre à ses goûts. Par conséquent, pour différents moteurs, les optimisations apportées sont très susceptibles d'être contradictoires.
Et si vous faites de la programmation Web multi-navigateurs, le plus gros problème est IE6 (JScript 5.6) ! Car sans hotfix, le bug de garbage collection du moteur JScript fera que ses performances dans les applications réelles ne seront pas du même ordre de grandeur que celles des autres navigateurs. Par conséquent, optimiser dans cette situation revient en fait à optimiser pour JScript !
Le premier principe est donc d'optimiser uniquement pour IE6 (JScript 5.6 non corrigé ou version antérieure) !
Si votre programme a été optimisé pour des performances acceptables sous IE6, il n'y aura fondamentalement aucun problème de performances sur les autres navigateurs.
Par conséquent, veuillez noter que bon nombre des problèmes dont je parle ci-dessous peuvent être complètement différents sur d'autres moteurs. Par exemple, l'épissage de chaînes dans une boucle est généralement considéré comme nécessitant Array.join, mais en raison de moteurs tels que SpiderMonkey, ils ont optimisé l'opération "+" sur les chaînes. Par conséquent, utiliser Array.join n'est pas aussi efficace que d'utiliser directement "+". Mais si l’on considère IE6, cette différence d’efficacité sur les autres navigateurs ne vaut pas du tout la peine d’être mentionnée.
L'optimisation JS présente toujours des similitudes avec l'optimisation dans d'autres langages. Par exemple, ne vous précipitez pas dans l’optimisation dès le début, cela n’a aucun sens. La clé de l’optimisation reste de se concentrer sur l’endroit le plus critique, à savoir le goulot d’étranglement. D’une manière générale, les goulots d’étranglement apparaissent toujours dans des boucles à grande échelle. Cela ne veut pas dire que les boucles elles-mêmes présentent des problèmes de performances, mais qu’elles peuvent rapidement amplifier d’éventuels problèmes de performances.
Le deuxième principe est donc de prendre les boucles à grande échelle comme objet d'optimisation principal.
Les principes d'optimisation suivants n'ont de sens que dans les boucles à grande échelle. Cela n'a fondamentalement aucun sens de faire une telle optimisation en dehors du corps de la boucle.
À l'heure actuelle, la plupart des moteurs JS sont interprétés et exécutés. Dans le cas d'une exécution interprétée, l'efficacité des appels de fonction est faible dans toutes les opérations. De plus, des chaînes d’héritage de prototypes trop profondes ou des références à plusieurs niveaux réduiront également l’efficacité. Dans JScript, la surcharge des références de niveau 10 représente environ la moitié de la surcharge d’un appel de fonction vide. La surcharge des deux est bien supérieure à celle d’opérations simples (telles que quatre opérations arithmétiques).
Le troisième principe est donc d'essayer d'éviter trop de niveaux de référence et des appels de méthodes multiples inutiles.
Il est important de noter que dans certains cas, ce qui semble être un accès à une propriété est en réalité un appel de méthode. Par exemple, toutes les propriétés DOM sont en réalité des méthodes. Lors du parcours d'une NodeList, l'accès de la condition de boucle à nodes.length ressemble à une lecture d'attribut, mais est en réalité équivalent à un appel de fonction. De plus, dans l'implémentation d'IE DOM, childNodes.length doit être recompté via un parcours interne à chaque fois. (Mon Dieu, mais c'est vrai ! Parce que j'ai mesuré que le temps d'accès de childNodes.length est proportionnel à la valeur de childNodes.length !) Cela coûte très cher. Par conséquent, enregistrer nodes.length dans une variable js à l'avance peut certainement améliorer les performances de traversée.
C'est aussi un appel de fonction, et l'efficacité des fonctions définies par l'utilisateur est bien inférieure à celle des fonctions intégrées au langage, car cette dernière est un wrapper pour les méthodes natives du moteur , et le moteur est généralement c, écrit en c++, java. De plus, pour la même fonction, le coût des constructions de langage intégrées est généralement plus efficace que celui des appels de fonctions intégrées, car les premiers peuvent être déterminés et optimisés pendant la phase d'analyse du code JS.
Le quatrième principe est donc d'essayer d'utiliser les constructions et les fonctions intégrées du langage lui-même.
Voici un exemple de la méthode String.format haute performance. L'implémentation traditionnelle de String.format consiste à utiliser String.replace(regex, func). Lorsque le modèle contient n espaces réservés (y compris ceux répétés), la fonction personnalisée func est appelée n fois. Dans cette implémentation hautes performances, chaque appel de format effectue uniquement une opération Array.join puis une opération String.replace(regex, string) Les deux sont des méthodes intégrées du moteur sans aucun appel de fonction personnalisé. Deux appels de méthode intégrés et un nombre n d'appels de méthode personnalisés, c'est la différence de performances.
sont également des fonctionnalités intégrées, mais il existe toujours des différences de performances. Par exemple, les performances d'accès aux arguments dans JScript sont très médiocres, rattrapant presque celles d'un appel de fonction. Par conséquent, si une fonction simple avec des paramètres variables devient un goulot d'étranglement en termes de performances, vous pouvez apporter des modifications internes et ne pas accéder aux arguments, mais les gérer via un jugement explicite des paramètres.
Par exemple :
Code Java
function sum() { var r = 0; for (var i = 0; i < arguments.length; i++) { r += arguments[i]; } return r; }
Cette somme est généralement appelée avec un petit nombre de paramètres, et nous espérons améliorer ses performances lorsqu'il y a moins de paramètres. Si modifié par :
Code Java
function sum() { switch (arguments.length) { case 1: return arguments[0]; case 2: return arguments[0] + arguments[1]; case 3: return arguments[0] + arguments[1] + arguments[2]; case 4: return arguments[0] + arguments[1] + arguments[2] + arguments[3]; default: var r = 0; for (var i = 0; i < arguments.length; i++) { r += arguments[i]; } return r; } }
En fait, cela ne s'améliorera pas beaucoup, mais s'il est modifié en :
Java代码
function sum(a, b, c, d, e, f, g) { var r = a ? b ? c ? d ? e ? f ? a + b + c + d + e + f : a + b + c + d + e : a + b + c + d : a + b + c : a + b : a : 0; if (g === undefined) return r; for (var i = 6; i < arguments.length; i++) { r += arguments[i]; } return r; }
就会提高很多(至少快1倍)。
最后是第五原则,也往往是真实应用中最重要的性能障碍,那就是尽量减少不必要的对象创建。
本身创建对象是有一定的代价的,但是这个代价其实并不大。最根本的问题是由于JScript愚蠢之极的垃圾回收调度算法,导致随着对象个数的增加,性能严重下降(据微软的人自己说复杂度是O(n^2))。
比如我们常见的字符串拼接问题,经过我的测试验证,单纯的多次创建字符串对象其实根本不是性能差的原因。要命的是在对象创建期间的无谓的垃圾回收的开销。而Array.join的方式,不会创建中间字符串对象,因此就减少了那该死的垃圾回收的开销。
因此,如果我们能把大规模对象创建转化为单一语句,则其性能会得到极大的提高!例如通过构造代码然后eval——实际上PIES项目中正在根据这个想法来做一个专门的大规模对象产生器……
好了上面就是总结的JS优化五大原则。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!