Maison >interface Web >js tutoriel >Rédaction de JavaScript hautes performances (traduction)_Conseils Javascript
Note du traducteur : c'est la première fois que je traduis une langue étrangère, donc la langue est forcément un peu obscure, mais j'ai fait de mon mieux pour exprimer l'intention originale de l'auteur sans polissage excessif. Les critiques et corrections sont les bienvenues. De plus, cet article est long et contient de nombreuses informations qui peuvent être difficiles à digérer. Vous êtes invités à laisser un message pour discuter des détails. Cet article se concentre principalement sur l'optimisation des Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascripts du V8, et certains contenus ne s'appliquent pas à tous les moteurs JS. Enfin, merci d'indiquer la source lors de la réimpression : )
========================= Ligne de démarcation de traduction ===================== == =======
De nombreux moteurs JavaScript, tels que le moteur V8 de Google (utilisé par Chrome et Node), sont spécialement conçus pour les grandes applications JavaScript qui nécessitent une exécution rapide. Si vous êtes un développeur et que vous êtes préoccupé par l'utilisation de la mémoire et les Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascripts des pages, vous devez comprendre comment fonctionne le moteur JavaScript du navigateur de l'utilisateur. Qu'il s'agisse de V8, SpiderMonkey (Firefox), Carakan (Opera), Chakra (IE) ou d'autres moteurs, cela peut vous aider à mieux optimiser votre application. Cela ne signifie pas que vous devez optimiser pour un navigateur ou un moteur spécifique, ne le faites pas. Vous devriez cependant vous poser quelques questions : Dans mon code, puis-je rendre le code plus efficace ? Quelles optimisations ont été effectuées par les moteurs JavaScript traditionnels
Qu'est-ce que le moteur ne peut pas optimiser ? Le garbage collector (GC) peut-il récupérer ce que j'attends ?
Un site Web rapide est comme une voiture de sport rapide, nécessitant des pièces spécialement personnalisées. Source de l'image :
.Il existe certains pièges courants lors de l'écriture de code haute Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascript, et dans cet article, nous allons vous montrer quelques façons éprouvées et meilleures de l'écrire. Alors, comment fonctionne JavaScript dans la V8 ?
Si vous n'avez pas une compréhension approfondie du moteur JS, il n'y a aucun problème à développer une application Web à grande échelle, tout comme une personne qui sait conduire n'a vu que le capot mais pas le moteur à l'intérieur. capot de voiture. Puisque Chrome est mon navigateur préféré, parlons de son moteur JavaScript. Le V8 est composé des parties principales suivantes :
V8 construit des objets dans le
Modèle d'objet
Le garbage collector tente de récupérer la mémoire Source de l'image : Valtteri Mäki. En JavaScript, il est impossible de forcer le garbage collection. Vous ne devriez pas faire cela, car le processus de récupération de place est contrôlé par le moteur d'exécution, qui sait quel est le meilleur moment pour nettoyer. Il existe de nombreuses discussions sur le recyclage de la mémoire JavaScript sur Internet à propos du mot-clé delete Bien qu'il puisse être utilisé pour supprimer des attributs (clés) dans des objets (cartes), certains développeurs pensent qu'il peut être utilisé pour forcer « Éliminer les références ». . Il est recommandé d'éviter d'utiliser delete si possible, dans l'exemple suivant Vous trouverez facilement la suppression des citations dans les bibliothèques JS populaires - c'est un langage utile. Ce qu'il faut noter ici, c'est éviter de modifier la structure des objets « chauds » au moment de l'exécution. Le moteur JavaScript peut détecter ces objets « chauds » et tenter de les optimiser. Si la structure de l'objet ne change pas de manière significative au cours de son cycle de vie, il sera plus facile pour le moteur d'optimiser l'objet, et l'opération de suppression déclenchera en fait des changements structurels aussi importants, ce qui n'est pas propice à l'optimisation du moteur. Il existe également des malentendus sur le fonctionnement de null. Définir une référence d'objet sur null ne rend pas l'objet "null", il définit simplement sa référence sur null. Il est préférable d'utiliser o.x=null que d'utiliser delete, mais cela n'est peut-être pas nécessaire. Si cette référence est la dernière référence à l'objet actuel, alors l'objet sera récupéré. Si cette référence n'est pas la dernière référence à l'objet actuel, l'objet est accessible et ne sera pas récupéré. Une autre chose à noter est que les variables globales ne sont pas nettoyées par le garbage collector pendant le cycle de vie de la page. Quelle que soit la durée d'ouverture de la page, les variables dans la portée globale de l'objet existeront toujours lorsque JavaScript est en cours d'exécution. Les objets globaux ne sont nettoyés que lorsque vous actualisez la page, accédez à une autre page, fermez l'onglet ou quittez le navigateur. Les variables dans la portée de la fonction seront effacées lorsqu'elles seront hors de portée, c'est-à-dire qu'à la sortie de la fonction, il n'y aura plus de références et ces variables seront effacées. Afin que le éboueur ramasse le plus d'objets possible le plus tôt possible, ne tenez pas les objets qui ne servent plus. Voici quelques points à retenir : Ensuite, parlons des fonctions. Comme nous l'avons dit, le garbage collection fonctionne en récupérant les blocs de mémoire (objets) qui ne sont plus accessibles. Pour mieux illustrer cela, voici quelques exemples. Au retour de foo, l'objet pointé par bar sera automatiquement recyclé par le garbage collector car il n'a plus de référence existante. Comparez : Nous avons maintenant une référence pointant vers l'objet bar, de sorte que le cycle de vie de l'objet bar dure de l'appel de foo jusqu'à ce que l'appelant spécifie une autre variable b (ou b sort hors de portée). Lorsque vous voyez une fonction qui renvoie une fonction interne, cette fonction interne obtiendra un accès hors de portée, même après l'exécution de la fonction externe. Il s'agit d'une fermeture de base — une expression qui peut définir une variable dans un contexte spécifique. Par exemple : L'objet fonction (sumIt) généré dans le contexte d'appel de somme ne peut pas être recyclé. Il est référencé par la variable globale (sumA) et peut être appelé via sumA(n). Regardons un autre exemple. Pouvons-nous accéder à la variable largeStr ici ? Oui, nous pouvons accéder à largeStr via a(), donc il n'est pas recyclé. Et celui ci-dessous ? Nous ne pouvons plus accéder à largeStr, c'est déjà un candidat au garbage collection. [Note du traducteur : Parce que largeStr n'a plus de références externes] 最糟的内存泄漏地方之一是在循环中,或者在setTimeout()/ setInterval()中,但这是相当常见的。思考下面的例子: 如果我们运行myObj.callMeMaybe();来启动定时器,可以看到控制台每秒打印出“Time is running out!”。如果接着运行 同样值得牢记的是,setTimeout/setInterval调用(如函数)中的引用,将需要执行和完成,才可以被垃圾收集。 永远不要优化代码,直到你真正需要。现在经常可以看到一些基准测试,显示N比M在V8中更为优化,但是在模块代码或应用中测试一下会发现,这些优化真正的效果比你期望的要小的多。 做的过多还不如什么都不做. 图片来源: Tim Sheerman-Chase. 比如我们想要创建这样一个模块: 这个问题有几个不同的因素,虽然也很容易解决。我们如何存储数据,如何高效地绘制表格并且append到DOM中,如何更优地处理表格事件? 面对这些问题最开始(天真)的做法是使用对象存储数据并放入数组中,使用jQuery遍历数据绘制表格并append到DOM中,最后使用事件绑定我们期望地点击行为。 注意:这不是你应该做的 这段代码简单有效地完成了任务。 但在这种情况下,我们遍历的数据只是本应该简单地存放在数组中的数字型属性ID。有趣的是,直接使用DocumentFragment和本地DOM方法比使用jQuery(以这种方式)来生成表格是更优的选择,当然,事件代理比单独绑定每个td具有更高的性能。 要注意虽然jQuery在内部使用DocumentFragment,但是在我们的例子中,代码在循环内调用append并且这些调用涉及到一些其他的小知识,因此在这里起到的优化作用不大。希望这不会是一个痛点,但请务必进行基准测试,以确保自己代码ok。 对于我们的例子,上述的做法带来了(期望的)性能提升。事件代理对简单的绑定是一种改进,可选的DocumentFragment也起到了助推作用。 接下来看看其他提升性能的方式。你也许曾经在哪读到过使用原型模式比模块模式更优,或听说过使用JS模版框架性能更好。有时的确如此,不过使用它们其实是为了代码更具可读性。对了,还有预编译!让我们看看在实践中表现的如何? 事实证明,在这种情况下的带来的性能提升可以忽略不计。模板和原型的选择并没有真正提供更多的东西。也就是说,性能并不是开发者使用它们的原因,给代码带来的可读性、继承模型和可维护性才是真正的原因。 更复杂的问题包括高效地在canvas上绘制图片和操作带或不带类型数组的像素数据。 在将一些方法用在你自己的应用之前,一定要多了解这些方案的基准测试。也许有人还记得JS模版的shoot-off和随后的扩展版。你要搞清楚基准测试不是存在于你看不到的那些虚拟应用,而是应该在你的实际代码中去测试带来的优化。 详细介绍了每个V8引擎的优化点在本文讨论范围之外,当然这里也有许多值得一提的技巧。记住这些技巧你就能减少那些性能低下的代码了。 更多内容可以去看Daniel Clifford在Google I/O的分享 Breaking the JavaScript Speed Limit with V8。 Optimizing For V8 — A Series也非常值得一读。 JavaScript中对象和数组之间只有一个的主要区别,那就是数组神奇的length属性。如果你自己来维护这个属性,那么V8中对象和数组的速度是一样快的。 对于应用程序开发人员,对象克隆是一个常见的问题。虽然各种基准测试可以证明V8对这个问题处理得很好,但仍要小心。复制大的东西通常是较慢的——不要这么做。JS中的for..in循环尤其糟糕,因为它有着恶魔般的规范,并且无论是在哪个引擎中,都可能永远不会比任何对象快。 当你一定要在关键性能代码路径上复制对象时,使用数组或一个自定义的“拷贝构造函数”功能明确地复制每个属性。这可能是最快的方式: 使用模块模式时缓存函数,可能会导致性能方面的提升。参阅下面的例子,因为它总是创建成员函数的新副本,你看到的变化可能会比较慢。 另外请注意,使用这种方法明显更优,不仅仅是依靠原型模式(经过jsPerf测试确认)。 使用模块模式或原型模式时的性能提升 这是一个原型模式与模块模式的性能对比测试: 接下来说说数组相关的技巧。在一般情况下,不要删除数组元素,这样将使数组过渡到较慢的内部表示。当索引变得稀疏,V8将会使元素转为更慢的字典模式。 数组字面量非常有用,它可以暗示VM数组的大小和类型。它通常用在体积不大的数组中。 将混合类型(比如数字、字符串、undefined、true/false)的数据存在数组中绝不是一个好想法。例如var arr = [1, “1”, undefined, true, “true”] 正如我们所看到的结果,整数的数组是最快的。 当你使用稀疏数组时,要注意访问元素将远远慢于满数组。因为V8不会分配一整块空间给只用到部分空间的数组。取而代之的是,它被管理在字典中,既节约了空间,但花费访问的时间。 不要预分配大数组(如大于64K的元素),其最大的大小,而应该动态分配。在我们这篇文章的性能测试之前,请记住这只适用部分JavaScript引擎。 空字面量与预分配数组在不同的浏览器进行测试 Nitro (Safari)对预分配的数组更有利。而在其他引擎(V8,SpiderMonkey)中,预先分配并不是高效的。 在Web应用的世界中,速度就是一切。没有用户希望用一个要花几秒钟计算某列总数或花几分钟汇总信息的表格应用。这是为什么你要在代码中压榨每一点性能的重要原因。 图片来源: Per Olof Forsberg. 理解和提高应用程序的性能是非常有用的同时,它也是困难的。我们推荐以下的步骤来解决性能的痛点: 下面推荐的一些工具和技术可以协助你。 有很多方式来运行JavaScript代码片段的基准测试其性能——一般的假设是,基准简单地比较两个时间戳。这中模式被jsPerf团队指出,并在SunSpider和Kraken的基准套件中使用: 在这里,要测试的代码被放置在一个循环中,并运行一个设定的次数(例如6次)。在此之后,开始日期减去结束日期,就得出在循环中执行操作所花费的时间。 然而,这种基准测试做的事情过于简单了,特别是如果你想运行在多个浏览器和环境的基准。垃圾收集器本身对结果是有一定影响的。即使你使用window.Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascript这样的解决方案,也必须考虑到这些缺陷。 不管你是否只运行基准部分的代码,编写一个测试套件或编码基准库,JavaScript基准其实比你想象的更多。如需更详细的指南基准,我强烈建议你阅读由Mathias Bynens和John-David Dalton提供的Javascript基准测试。 Chrome开发者工具为JavaScript分析有很好的支持。可以使用此功能检测哪些函数占用了大部分时间,这样你就可以去优化它们。这很重要,即使是代码很小的改变会对整体表现产生重要的影响。 Panneau d'analyse des outils de développement Chrome Le processus d'analyse commence par l'obtention de la référence de Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascript du code, qui se reflète ensuite sous la forme d'une chronologie. Cela nous dira combien de temps le code met à s'exécuter. L'onglet "Profils" nous donne une meilleure vue de ce qui se passe dans l'application. Le profil CPU JavaScript indique la quantité de temps CPU utilisée par notre code, le profil de sélecteur CSS indique le temps passé à traiter les sélecteurs et l'instantané du tas indique la quantité de mémoire utilisée pour nos objets. Grâce à ces outils, nous pouvons isoler, ajuster et réanalyser pour mesurer si nos optimisations de Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascripts fonctionnelles ou opérationnelles ont réellement un impact. L'onglet "Profil" affiche des informations sur les Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascripts du code. Pour une bonne introduction au profilage, lisez JavaScript Profiling With The Chrome Developer Tools de Zack Grossbart. Conseil : Idéalement, si vous souhaitez vous assurer que votre analyse n'est en aucun cas affectée par les applications ou extensions installées, lancez Chrome avec le drapeau Au sein de Google, les outils de développement Chrome sont largement utilisés par des équipes telles que Gmail pour aider à détecter et résoudre les fuites de mémoire. Statistiques de mémoire dans les outils de développement Chrome Les statistiques de mémoire incluent l'utilisation de la mémoire privée, la taille du tas JavaScript, le nombre de nœuds DOM, le nettoyage du stockage, les compteurs d'écoute d'événements et les éléments sur le point d'être recyclés par le garbage collector qui intéressent notre équipe. Il est recommandé de lire la Technique "3 Snapshot" de Loreena Lee. L'essentiel de cette technique est d'enregistrer un comportement dans votre application, de forcer un garbage collection, de vérifier que le nombre de nœuds DOM est revenu à la ligne de base attendue, puis d'analyser trois instantanés de tas pour déterminer s'il y a des fuites de mémoire. La gestion de la mémoire est très importante pour les applications à page unique (par exemple AngularJS, Backbone, Ember), elles ne rafraîchissent presque jamais la page. Cela signifie que les fuites de mémoire peuvent être assez évidentes. Les applications d'une seule page sur les appareils mobiles sont semées d'embûches car l'appareil dispose d'une mémoire limitée et d'applications qui s'exécutent longtemps telles que les clients de messagerie ou les réseaux sociaux. Plus la capacité est grande, plus la responsabilité est grande. Il existe de nombreuses façons de résoudre ce problème. Dans Backbone, assurez-vous d'utiliser dispose() pour supprimer les anciennes vues et références (actuellement disponibles dans Backbone(Edge)). Cette fonction a été récemment ajoutée et supprime les gestionnaires ajoutés à l'objet "event" de la vue, ainsi que les écouteurs d'événements pour le modèle ou la collection transmis au troisième argument de la vue (le contexte de rappel). dispose() est également appelé par Remove() de la vue et gère le travail de nettoyage principal lorsque l'élément est supprimé. D'autres bibliothèques telles que Ember nettoieront les écouteurs lorsqu'elles détecteront qu'un élément a été supprimé pour éviter les fuites de mémoire. Quelques conseils avisés de Derick Bailey : 与其了解事件与引用是如何工作的,不如遵循的标准规则来管理JavaScript中的内存。如果你想加载数据到的一个存满用户对象的Backbone集合中,你要清空这个集合使它不再占用内存,那必须这个集合的所有引用以及集合内对象的引用。一旦清楚了所用的引用,资源就会被回收。这就是标准的JavaScript垃圾回收规则。 在文章中,Derick涵盖了许多使用Backbone.js时的常见内存缺陷,以及如何解决这些问题。 Felix Geisendörfer的在Node中调试内存泄漏的教程也值得一读,尤其是当它形成了更广泛SPA堆栈的一部分。 当浏览器重新渲染文档中的元素时需要 重新计算它们的位置和几何形状,我们称之为回流。回流会阻塞用户在浏览器中的操作,因此理解提升回流时间是非常有帮助的。 回流时间图表 你应该批量地触发回流或重绘,但是要节制地使用这些方法。尽量不处理DOM也很重要。可以使用DocumentFragment,一个轻量级的文档对象。你可以把它作为一种方法来提取文档树的一部分,或创建一个新的文档“片段”。与其不断地添加DOM节点,不如使用文档片段后只执行一次DOM插入操作,以避免过多的回流。 例如,我们写一个函数给一个元素添加20个div。如果只是简单地每次append一个div到元素中,这会触发20次回流。 要解决这个问题,可以使用DocumentFragment来代替,我们可以每次添加一个新的div到里面。完成后将DocumentFragment添加到DOM中只会触发一次回流。 可以参阅 Make the Web Faster,JavaScript Memory Optimization 和 Finding Memory Leaks。 为了帮助发现JavaScript内存泄漏,谷歌的开发人员((Marja Hölttä和Jochen Eisinger)开发了一种工具,它与Chrome开发人员工具结合使用,检索堆的快照并检测出是什么对象导致了内存泄漏。 一个JavaScript内存泄漏检测工具 有完整的文章介绍了如何使用这个工具,建议你自己到内存泄漏探测器项目页面看看。 如果你想知道为什么这样的工具还没集成到我们的开发工具,其原因有二。它最初是在Closure库中帮助我们捕捉一些特定的内存场景,它更适合作为一个外部工具。 Chrome支持直接通过传递一些标志给V8,以获得更详细的引擎优化输出结果。例如,这样可以追踪V8的优化: Windows用户可以这样运行 chrome.exe –js-flags=”–trace-opt –trace-deopt” 在开发应用程序时,下面的V8标志都可以使用。 Le script de traitement de V8 utilise * (astérisque) pour identifier les fonctions optimisées et ~ (tilde) pour indiquer les fonctions non optimisées. Si vous souhaitez en savoir plus sur les indicateurs du V8 et sur le fonctionnement des composants internes du V8, il est fortement recommandé de lire l'excellent article de Vyacheslav Egorov sur les composants internes du V8. Heure de haute précision (HRT) est une interface temporelle de haute précision inférieure à la milliseconde qui n'est pas affectée par l'heure du système et les ajustements de l'utilisateur. Elle peut être considérée comme plus avancée que la nouvelle date et la nouvelle date. now() Méthodes de mesure précises. Cela nous aide beaucoup lors de la rédaction de benchmarks. Le temps de haute précision (HRT) fournit une précision temporelle actuelle inférieure à la milliseconde Actuellement, HRT est utilisé dans Chrome (version stable) dans la méthode window.Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascript.webkitNow(), mais le préfixe est supprimé dans Chrome Canary, ce qui permet de l'appeler dans window.Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascript.now() méthode. Paul Irish a écrit plus sur HRT sur HTML5Rocks. Maintenant que nous connaissons l'heure précise actuelle, existe-t-il une API capable de mesurer avec précision les Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascripts des pages ? Eh bien, il existe désormais une API de synchronisation de navigation disponible. Cette API fournit un moyen simple d'obtenir des enregistrements de mesure du temps précis et détaillés lorsqu'une page Web est chargée et présentée à l'utilisateur. Vous pouvez utiliser window.Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascript.timing dans la console pour obtenir des informations temporelles : Informations horaires affichées dans la console Nous pouvons obtenir de nombreuses informations utiles à partir des données ci-dessus, telles que le délai de réseau comme ResponseEnd – FetchStart, le temps de chargement des pages comme LoadEventEnd – ResponseEnd, et le traitement de la navigation et du temps de chargement des pages comme LoadEventEnd – navigationStart. Comme vous pouvez le constater, la propriété Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascript.memory peut également afficher l'utilisation des données de la mémoire JavaScript, telle que la taille totale du tas. Pour plus de détails sur l'API Navigation Timing, lisez Mesurer la vitesse de chargement des pages avec le timing de navigation de Sam Dutton. À propos : le traçage dans Chrome fournit une vue des Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascripts du navigateur, en enregistrant tous les fils de discussion, pages à onglets et processus de Chrome. Cet outil est vraiment utile pour vous permettre de capturer les données en cours d'exécution de Chrome afin que vous puissiez ajuster l'exécution de JavaScript de manière appropriée ou optimiser le chargement des ressources. Lilli Thompson a un article écrit pour les développeurs de jeux sur l'utilisation de about:Rédaction de JavaScript hautes performances (traduction)_Conseils Javascript pour analyser les jeux WebGL. Il convient également aux développeurs JavaScript. Vous pouvez saisir about:memory dans la barre de navigation de Chrome, ce qui est également très pratique. Vous pouvez obtenir l'utilisation de la mémoire de chaque page à onglet, ce qui est très utile pour localiser les fuites de mémoire. Nous constatons qu'il existe de nombreux pièges cachés dans le monde JavaScript, et qu'il n'existe pas de solution miracle pour améliorer les Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascripts. Ce n'est qu'en appliquant de manière exhaustive certaines solutions d'optimisation à l'environnement de test (du monde réel) que des gains de Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascripts optimaux peuvent être obtenus. Néanmoins, comprendre comment le moteur interprète et optimise le code peut vous aider à affiner votre application. Mesurer, comprendre, réparer. Continuez à répéter ce processus. Source de l'image : Sally Hunter N'oubliez pas de faire attention à l'optimisation, mais vous pouvez ignorer quelques petites optimisations pour plus de commodité. Par exemple, certains développeurs choisissent .forEach et Object.keys au lieu des boucles for et for..in, qui sont plus lentes mais plus pratiques à utiliser. Assurez-vous d'avoir l'esprit clair et de savoir quelles optimisations sont nécessaires et quelles optimisations ne le sont pas. Notez également que même si les moteurs JavaScript deviennent plus rapides, le prochain véritable goulot d'étranglement est le DOM. La réduction des Rédaction de JavaScript hautes Rédaction de JavaScript hautes Rédaction de JavaScript hautes performances (traduction)_Conseils Javascripts (traduction)_Conseils Javascripts (traduction)_Conseils Javascripts et des redraws est également importante, alors ne touchez pas au DOM avant que cela ne soit nécessaire. Une autre chose à laquelle il faut prêter attention est que les requêtes HTTP du réseau sont précieuses, en particulier sur les terminaux mobiles, la mise en cache HTTP doit donc être utilisée pour réduire la charge des ressources. En vous souvenant de ces points, vous obtiendrez la plupart des informations contenues dans cet article. J'espère qu'il vous sera utile ! Texte original : http://coding.smashingmagazine.com/2012/11/05/writing-fast-memory-efficient-javascript/ Auteur : Addy OsmaniLe malentendu de « l’élimination des citations »
delete o.x 的弊大于利,因为它改变了o的隐藏类,并使它成为一个"慢对象"。
var o = { x: 1 };
delete o.x; // true
o.x; // undefined
var o = { x: 1 };
o = null;
o; // null
o.x // TypeError
var myGlobalNamespace = {};
Règles empiriques
Fonction
function foo() {
var bar = new LargeObject();
bar.someCall();
}
function foo() {
var bar = new LargeObject();
bar.someCall();
return bar;
}
// somewhere else
var b = foo();
FERMETURES
function sum (x) {
function sumIt(y) {
return x + y;
};
return sumIt;
}
// Usage
var sumA = sum(4);
var sumB = sumA(3);
console.log(sumB); // Returns 7
var a = function () {
var largeStr = new Array(1000000).join('x');
return function () {
return largeStr;
};
}();
var a = function () {
var smallStr = 'x';
var largeStr = new Array(1000000).join('x');
return function (n) {
return smallStr;
};
}();
定时器
var myObj = {
callMeMaybe: function () {
var myRef = this;
var val = setTimeout(function () {
console.log('Time is running out!');
myRef.callMeMaybe();
}, 1000);
}
};
myObj =
null,定时器依旧处于激活状态。为了能够持续执行,闭包将myObj传递给setTimeout,这样myObj是无法被回收的。相反,它引用到myObj的因为它捕获了myRef。这跟我们为了保持引用将闭包传给其他的函数是一样的。
当心性能陷阱
var moduleA = function () {
return {
data: dataArrayObject,
init: function () {
this.addTable();
this.addEvents();
},
addTable: function () {
for (var i = 0; i < rows; i++) {
$tr = $('<tr></tr>');
for (var j = 0; j < this.data.length; j++) {
$tr.append('<td>' + this.data[j]['id'] + '</td>');
}
$tr.appendTo($tbody);
}
},
addEvents: function () {
$('table td').on('click', function () {
$(this).toggleClass('active');
});
}
};
}();
var moduleD = function () {
return {
data: dataArray,
init: function () {
this.addTable();
this.addEvents();
},
addTable: function () {
var td, tr;
var frag = document.createDocumentFragment();
var frag2 = document.createDocumentFragment();
for (var i = 0; i < rows; i++) {
tr = document.createElement('tr');
for (var j = 0; j < this.data.length; j++) {
td = document.createElement('td');
td.appendChild(document.createTextNode(this.data[j]));
frag2.appendChild(td);
}
tr.appendChild(frag2);
frag.appendChild(tr);
}
tbody.appendChild(frag);
},
addEvents: function () {
$('table').on('click', 'td', function () {
$(this).toggleClass('active');
});
}
};
}();
moduleG = function () {};
moduleG.prototype.data = dataArray;
moduleG.prototype.init = function () {
this.addTable();
this.addEvents();
};
moduleG.prototype.addTable = function () {
var template = _.template($('#template').text());
var html = template({'data' : this.data});
$tbody.append(html);
};
moduleG.prototype.addEvents = function () {
$('table').on('click', 'td', function () {
$(this).toggleClass('active');
});
};
var modG = new moduleG();
V8优化技巧
function add(x, y) {
return x+y;
}
add(1, 2);
add('a','b');
add(my_custom_object, undefined);
对象VS数组:我应该用哪个?
使用对象时的技巧
对象克隆
function clone(original) {
this.foo = original.foo;
this.bar = original.bar;
}
var copy = new clone(original);
模块模式中缓存函数
// Prototypal pattern
Klass1 = function () {}
Klass1.prototype.foo = function () {
log('foo');
}
Klass1.prototype.bar = function () {
log('bar');
}
// Module pattern
Klass2 = function () {
var foo = function () {
log('foo');
},
bar = function () {
log('bar');
};
return {
foo: foo,
bar: bar
}
}
// Module pattern with cached functions
var FooFunction = function () {
log('foo');
};
var BarFunction = function () {
log('bar');
};
Klass3 = function () {
return {
foo: FooFunction,
bar: BarFunction
}
}
// Iteration tests
// Prototypal
var i = 1000,
objs = [];
while (i--) {
var o = new Klass1()
objs.push(new Klass1());
o.bar;
o.foo;
}
// Module pattern
var i = 1000,
objs = [];
while (i--) {
var o = Klass2()
objs.push(Klass2());
o.bar;
o.foo;
}
// Module pattern with cached functions
var i = 1000,
objs = [];
while (i--) {
var o = Klass3()
objs.push(Klass3());
o.bar;
o.foo;
}
// See the test for full details
使用数组时的技巧
数组字面量
// Here V8 can see that you want a 4-element array containing numbers:
var a = [1, 2, 3, 4];
// Don't do this:
a = []; // Here V8 knows nothing about the array
for(var i = 1; i <= 4; i++) {
a.push(i);
}
存储单一类型VS多类型
稀疏数组与满数组
预分配空间VS动态分配
// Empty array
var arr = [];
for (var i = 0; i < 1000000; i++) {
arr[i] = i;
}
// Pre-allocated array
var arr = new Array(1000000);
for (var i = 0; i < 1000000; i++) {
arr[i] = i;
}
优化你的应用
基准化(BENCHMARKING)
var totalTime,
start = new Date,
iterations = 1000;
while (iterations--) {
// Code snippet goes here
}
// totalTime → the number of milliseconds taken
// to execute the code snippet 1000 times
totalTime = new Date - start;
分析(PROFILING)
--user-data-dir <empty_directory></empty_directory>
. Dans la plupart des cas, cette méthode d’optimisation de vos tests devrait être suffisante, mais nécessitera également plus de temps. C'est là que le logo V8 peut aider. Éviter les fuites de mémoire - 3 techniques de snapshot
Gestion de la mémoire pour les applications monopage
减少回流(REFLOWS)
function addDivs(element) {
var div;
for (var i = 0; i < 20; i ++) {
div = document.createElement('div');
div.innerHTML = 'Heya!';
element.appendChild(div);
}
}
function addDivs(element) {
var div;
// Creates a new empty DocumentFragment.
var fragment = document.createDocumentFragment();
for (var i = 0; i < 20; i ++) {
div = document.createElement('a');
div.innerHTML = 'Heya!';
fragment.appendChild(div);
}
element.appendChild(fragment);
}
JS内存泄漏探测器
V8优化调试和垃圾回收的标志位
"/Applications/Google Chrome/Google Chrome" --js-flags="--trace-opt --trace-deopt"
API DE TEMPS HAUTE RÉSOLUTION et DE TEMPS DE NAVIGATION
À PROPOS DE : MÉMOIRE et À PROPOS : DE TRACAGE
About:Tracing提供了浏览器的性能视图
Résumé