ホームページ > 記事 > ウェブフロントエンド > 高性能 JavaScript の書き方 (翻訳)_JavaScript のヒント
翻訳者注: 外国語を翻訳するのは初めてなので、どうしても言葉が曖昧になってしまいますが、過度に磨きをかけることなく、著者の本来の意図を表現するように努めています。批判や修正は歓迎です。また、この記事は長く、多くの情報が含まれているため、理解するのが難しい場合があります。詳細についてはメッセージを残してください。この記事は主に V8 のパフォーマンスの最適化に焦点を当てており、一部の内容はすべての JS エンジンに適用されるわけではありません。最後に、転載する場合は出典を明記してください:)
========================翻訳分割線==================== == =======
Google の V8 エンジン (Chrome と Node で使用) などの多くの JavaScript エンジンは、高速実行 を必要とする大規模な JavaScript アプリケーション向けに特別に設計されています。あなたが開発者で、メモリ使用量とページのパフォーマンスに懸念がある場合は、ユーザーのブラウザの JavaScript エンジンがどのように動作するかを理解しておく必要があります。 V8、SpiderMonkey (Firefox)、Carakan (Opera)、Chakra (IE)、またはその他のエンジンのいずれであっても、これを実行すると、アプリをより適切に最適化するのに役立ちます。これは、特定のブラウザやエンジンに合わせて最適化する必要があるという意味ではありません。そんなことはしないでください。 ただし、いくつかの質問を自問する必要があります。 私のコードで、コードをより効率的にすることはできますか? 主流の JavaScript エンジンによって行われた最適化
エンジンが最適化できないものは何ですか? ガベージ コレクター (GC) は期待どおりに回復できますか?
高速な Web サイトは高速なスポーツカーのようなもので、特別にカスタマイズされたパーツが必要です。画像ソース:
.。 高パフォーマンスのコードを作成する際にはよくある落とし穴がいくつかあります。この記事では、コードを作成するための実証済みのより良い方法をいくつか紹介します。 それでは、JavaScript は V8 でどのように動作するのでしょうか?
JS エンジンについて深く理解していなくても、車の運転を知っている人がボンネットだけ見て車の中のエンジンを見ていないのと同じように、大規模な Web アプリケーションの開発には問題ありません。車のボンネット。 Chrome が私が選んだブラウザなので、その JavaScript エンジンについて話しましょう。 V8 は次のコア部分で構成されています:
V8 はオブジェクトを
オブジェクト モデル
ガベージ コレクターがメモリを再利用しようとしています。画像ソース: Valtteri Mäki. JavaScript では、ガベージ コレクションを強制することはできません。ガベージ コレクション プロセスはランタイムによって制御されており、ランタイムはクリーンアップに最適な時期を認識しているため、これを行うべきではありません。 インターネット上では、JavaScript のメモリ リサイクルについて、delete キーワードについて多くの議論が行われていますが、このキーワードはオブジェクト (マップ) 内の属性 (キー) を削除するために使用できますが、一部の開発者は、「参照の削除」を強制するために使用できると考えています。 。次の例では、可能な限り delete の使用を避けることをお勧めします 人気のある JS ライブラリで引用符の削除を簡単に見つけることができます。これは言語の目的に基づいたものです。ここで注意する必要があるのは、実行時に「ホット」オブジェクトの構造を変更しないようにすることです。 JavaScript エンジンは、そのような「ホット」オブジェクトを検出し、それらの最適化を試みることができます。オブジェクトの構造がライフサイクル中に大幅に変化しない場合、エンジンによるオブジェクトの最適化は容易になりますが、削除操作によって実際にそのような大きな構造変化が引き起こされるため、エンジンの最適化には役立ちません。 null の仕組みについても誤解があります。オブジェクト参照を null に設定しても、オブジェクトは「null」にはならず、その参照が null に設定されるだけです。 o.x=null を使用することは、delete を使用するよりも優れていますが、必ずしも必要ではない場合もあります。 この参照が現在のオブジェクトへの最後の参照である場合、オブジェクトはガベージ コレクションされます。この参照が現在のオブジェクトへの最後の参照ではない場合、そのオブジェクトはアクセス可能であり、ガベージ コレクションされません。 もう 1 つ注意すべき点は、グローバル変数はページのライフサイクル中にガベージ コレクターによってクリーンアップされないことです。ページが開いている時間に関係なく、JavaScript の実行中はグローバル オブジェクト スコープ内の変数が常に存在します。 グローバル オブジェクトは、ページを更新するか、別のページに移動するか、タブを閉じるか、ブラウザを終了するときにのみクリーンアップされます。関数スコープ内の変数はスコープ外に出るとクリアされます。つまり、関数を終了すると参照がなくなり、そのような変数はクリアされます。 ガベージ コレクターができるだけ早くできるだけ多くのオブジェクトを収集するために、使用されなくなったオブジェクトを保持しないでください。覚えておくべきことがいくつかあります: 次に関数について説明します。すでに述べたように、ガベージ コレクションは、アクセスできなくなったメモリ ブロック (オブジェクト) を再利用することによって機能します。これをわかりやすく説明するために、いくつかの例を示します。 foo が返されると、bar が指すオブジェクトは既存の参照がなくなったため、ガベージ コレクターによって自動的にリサイクルされます。 比較: これで、bar オブジェクトを指す参照ができました。そのため、bar オブジェクトのライフサイクルは、foo の呼び出しから呼び出し元が別の変数 b を指定する (または b がスコープ外になる) まで続きます。 内部関数を返す関数がある場合、外部関数が実行された後でも、その内部関数はスコープ外のアクセスを取得します。これは基本的な クロージャ、つまり特定のコンテキストで変数を設定できる式です。例: sum 呼び出しコンテキストで生成された関数オブジェクト (sumIt) は再利用できません。グローバル変数 (sumA) によって参照され、sumA(n) を通じて呼び出すことができます。 別の例を見てみましょう。ここで変数largeStrにアクセスできますか? はい、a() 経由でlargeStr にアクセスできるため、リサイクルされません。以下のものはどうでしょうか? largeStr にはアクセスできなくなりました。すでにガベージ コレクションの候補になっています。 [翻訳者注:largeStr には外部参照がなくなったため] 最糟的内存泄漏地方之一是在循环中,或者在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.高性能 JavaScript の書き方 (翻訳)_JavaScript のヒント这样的解决方案,也必须考虑到这些缺陷。 不管你是否只运行基准部分的代码,编写一个测试套件或编码基准库,JavaScript基准其实比你想象的更多。如需更详细的指南基准,我强烈建议你阅读由Mathias Bynens和John-David Dalton提供的Javascript基准测试。 Chrome开发者工具为JavaScript分析有很好的支持。可以使用此功能检测哪些函数占用了大部分时间,这样你就可以去优化它们。这很重要,即使是代码很小的改变会对整体表现产生重要的影响。 Chrome デベロッパー ツール分析パネル 分析プロセスは、コード パフォーマンスのベースラインを取得することから始まり、それがタイムラインの形で反映されます。これにより、コードの実行にかかる時間がわかります。 [プロファイル] タブを使用すると、アプリケーションで何が起こっているかがよくわかります。 JavaScript CPU プロファイルはコードによって使用されている CPU 時間を示し、CSS セレクター プロファイルはセレクターの処理に費やされた時間を示し、ヒープ スナップショットはオブジェクトに使用されているメモリの量を示します。 これらのツールを使用すると、機能または運用パフォーマンスの最適化が実際に影響を与えているかどうかを測定するために、分離、調整、再分析を行うことができます。 「プロファイル」タブには、コードのパフォーマンス情報が表示されます。 プロファイリングの入門書としては、Zack Grossbart の Chrome Developer Tools を使用した JavaScript プロファイリング をお読みください。 ヒント: インストールされているアプリや拡張機能によって分析がまったく影響を受けないようにする場合は、 Google 内では、メモリ リークの発見とトラブルシューティングを支援するために、Gmail などのチームによって Chrome 開発者ツールが頻繁に使用されています。 Chrome デベロッパー ツールのメモリ統計 メモリ統計には、プライベート メモリの使用量、JavaScript ヒープ サイズ、DOM ノードの数、ストレージのクリーンアップ、イベント リスニング カウンター、チームが関心を持っているガベージ コレクターによってリサイクルされる予定のものが含まれます。 Loreena Lee の 「3 スナップショット」テクニック を読むことをお勧めします。この手法の要点は、アプリケーションの動作をログに記録し、ガベージ コレクションを強制的に実行し、DOM ノードの数が予想されるベースラインに戻ったことを確認してから、3 つのヒープ スナップショットを分析してメモリ リークがあるかどうかを判断することです。 単一ページ アプリケーション (AngularJS、Backbone、Ember など) ではメモリ管理が非常に重要であり、ページを更新することはほとんどありません。これは、メモリ リークが非常に明白である可能性があることを意味します。モバイル端末上のシングルページ アプリケーションには落とし穴がたくさんあります。これは、デバイスのメモリが限られており、電子メール クライアントやソーシャル ネットワークなどの長時間実行されるアプリケーションがあるためです。 能力が大きければ大きいほど、責任も大きくなります。 この問題を解決する方法はたくさんあります。 Backbone では、必ず destroy() を使用して古いビューと参照を破棄してください (現在は Backbone(Edge) で利用可能です)。この関数は最近追加され、ビューの「イベント」オブジェクトに追加されたハンドラーと、ビューの 3 番目の引数 (コールバック コンテキスト) に渡されたモデルまたはコレクションのイベント リスナーを削除します。 destroy() はビューの Remove() によっても呼び出され、要素が削除されたときに主なクリーンアップ作業を処理します。 Ember などの他のライブラリは、要素が削除されたことを検出すると、メモリ リークを避けるためにリスナーをクリーンアップします。 デリック・ベイリーからの賢明なアドバイス: 与其了解事件与引用是如何工作的,不如遵循的标准规则来管理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标志都可以使用。 V8 の処理スクリプトでは、* (アスタリスク) を使用して最適化された関数を識別し、~ (チルダ) を使用して最適化されていない関数を示します。 V8 のフラグと V8 の内部がどのように機能するかについて詳しく知りたい場合は、Vyacheslav Egorov の V8 の内部に関する優れた投稿を読むことを強くお勧めします。 高精度時刻 (HRT) は、システム時刻やユーザー調整の影響を受けない、ミリ秒未満の高精度時刻インターフェイスであり、新しい日付と日付よりも高度なものと見なされます。 now() 正確な測定方法。これは、ベンチマークを作成するときに非常に役立ちます。 高精度時間 (HRT) は、ミリ秒未満の現在の時間精度を提供します 現在、HRT は Chrome (安定版) で window.高性能 JavaScript の書き方 (翻訳)_JavaScript のヒント.webkitNow() を通じて使用されますが、Chrome Canary ではプレフィックスが破棄されるため、window.高性能 JavaScript の書き方 (翻訳)_JavaScript のヒント.now() を通じて呼び出すことができます。 Paul Irish は、HTML5Rocks で HRT についてさらに詳しく を書きました。 現在の正確な時間がわかったところで、ページのパフォーマンスを正確に測定できる API はあるのでしょうか? Navigation Timing API が利用可能になりました。この API は、Web ページがロードされてユーザーに表示されるときに、正確かつ詳細な時間測定記録を取得する簡単な方法を提供します。コンソールで window.高性能 JavaScript の書き方 (翻訳)_JavaScript のヒント.timing を使用して時間情報を取得できます: コンソールに表示される時刻情報 上記のデータから、ネットワーク遅延 (responseEnd – fetchStart として)、ページ読み込み時間 (loadEventEnd – responseEnd として)、処理ナビゲーションとページ読み込み時間 (loadEventEnd – NavigationStart として) など、多くの有用な情報を取得できます。 ご覧のとおり、高性能 JavaScript の書き方 (翻訳)_JavaScript のヒント.memory プロパティでは、合計ヒープ サイズなどの JavaScript メモリ データ使用量も表示できます。 Navigation Timing API の詳細については、Sam Dutton の Measuring Page Load Speed With Navigation Timing を参照してください。 Chrome の About:高性能 JavaScript の書き方 (翻訳)_JavaScript のヒント は、ブラウザのパフォーマンス ビューを提供し、Chrome のすべてのスレッド、タブ ページ、プロセスを記録します。 このツールが本当に役立つのは、Chrome の実行データをキャプチャして、JavaScript の実行を適切に調整したり、リソースの読み込みを最適化したりできることです。 Lilli Thompson は、WebGL ゲームを分析するための about:高性能 JavaScript の書き方 (翻訳)_JavaScript のヒント の使用について、ゲーム開発者向けに を書いています。これは JavaScript 開発者にも適しています。 Chrome のナビゲーション バーに「about:memory」と入力すると、各タブ ページのメモリ使用量を取得でき、メモリ リークを特定するのに非常に役立ちます。 JavaScript の世界には 多くの隠れた罠があり、パフォーマンスを向上させる特効薬はないことがわかります。いくつかの最適化ソリューションを (現実世界の) テスト環境に包括的に適用することによってのみ、最大のパフォーマンス向上を得ることができます。それでも、エンジンがコードをどのように解釈して最適化するかを理解すると、アプリケーションを調整するのに役立ちます。 測定、理解、修正。 このプロセスを繰り返します。 画像出典: サリー・ハンター 最適化に注意を払うことを忘れないでください。ただし、便宜上、いくつかの小さな最適化を破棄することもできます。たとえば、一部の開発者は、for および for..in ループの代わりに .forEach および Object.keys を選択します。これらのループは遅いですが、より使いやすいです。頭をすっきりさせて、どの最適化が必要で、どの最適化が不要であるかを理解してください。 また、JavaScript エンジンは高速化していますが、次の本当のボトルネックは DOM であることにも注意してください。リフローと再描画を減らすことも重要なので、必要になるまで DOM には触れないでください。もう 1 つ注意すべき点は、HTTP リクエストは特にモバイル端末では貴重であるため、リソースの負荷を軽減するために HTTP キャッシュを使用する必要があることです。 これらの点を覚えておくと、この記事のほとんどの情報が確実に得られると思います。 原文: http://coding.smashingmagazine.com/2012/11/05/writing-fast-memory-efficient-javascript/ 著者: アディ・オスマニ「引用の排除」の誤解
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 = {};
経験則
機能
function foo() {
var bar = new LargeObject();
bar.someCall();
}
function foo() {
var bar = new LargeObject();
bar.someCall();
return bar;
}
// somewhere else
var b = foo();
閉店
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>
フラグを指定して Chrome を起動するのが理想的です。ほとんどの場合、テストを最適化するこの方法で十分ですが、より多くの時間がかかります。ここで V8 ロゴが役に立ちます。 メモリ リークを回避する - 3 つのスナップショット手法
シングルページ アプリケーションのメモリ管理
减少回流(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
メモリについてとトレースについて
About:Tracing提供了浏览器的性能视图
概要