JavaScript のパフォーマンスの最適化は、決して書かれた手法ではありません。Nicholas の技術進化では、効率的な JS コードを作成するための 10 の提案がリストされています
1. ローカル変数を定義します
変数が参照されると、JavaScript はこの変数を別の場所で検索します。スコープチェーンのメンバー。スコープ チェーンは、現在そのスコープで動作している利用可能な変数のセットを指します。これには、ローカル変数のセットとグローバル変数のセットという、さまざまな主流ブラウザーで少なくとも 2 つの部分が含まれています。
簡単に言うと、JavaScript エンジンがスコープ チェーンのより深い部分を検索すると、操作に時間がかかります。エンジンはまずこれを起点としてローカル変数を検索し、次に関数パラメーター、ローカルに定義された変数を検索し、最後にすべてのグローバル変数を反復処理します。
ローカル変数はこのチェーンの先頭にあるため、ローカル変数の検索はグローバル変数の検索よりも常に高速です。したがって、グローバル変数を複数回使用したい場合は、次のようにローカル変数として定義する必要があります。
var blah = document.getElementById('myID'), blah2 = document.getElementById('myID2');
を
var doc = document, blah = doc.getElementById('myID'), blah2 = doc.getElementById('myID2');
として書き換えます。 2. with() ステートメントを使用しないでください。
これは、with() ステートメントがスコープ チェーンの先頭に追加の変数を追加するためです。追加の変数は、変数にアクセスする必要がある場合、JavaScript エンジンが最初に with() ステートメントによって生成された変数をスキャンし、次にローカル変数、最後にグローバル変数をスキャンする必要があることを意味します。
つまり、with() は本質的にローカル変数にグローバル変数のすべてのパフォーマンス上の欠点を与え、その結果、JavaScript の最適化が失敗します。
3. クロージャは慎重に使用してください
「クロージャ」についてまだ知らないかもしれませんが、気づかないうちにこのテクニックを頻繁に使用しているかもしれません。クロージャは基本的に JavaScript では新しいものとみなされます。次のような即時関数を定義するときにクロージャを使用します。 、およびグローバル変数。これらの余分なオブジェクトは、提案 1 と 2 で説明したパフォーマンスの問題を引き起こします。
しかし、ニコラスは窒息するから食べるのをやめるよう求めているわけではないと思います。クロージャーはコードの可読性を向上させるなどには依然として非常に役立ちますが、(特にループ内で) 乱用しないでください。
4. オブジェクトのプロパティと配列要素は変数よりも遅いJavaScript データに関しては、通常、値、変数、オブジェクト プロパティ、配列要素の 4 つのアクセス方法があります。最適化を考慮すると、数値と変数のパフォーマンスはほぼ同じであり、オブジェクトのプロパティや配列要素よりも大幅に高速です。
そのため、オブジェクトのプロパティまたは配列要素を複数回参照する場合、変数を定義することでパフォーマンスを向上させることができます。 (このルールはデータの読み書き時に有効です)このルールはほとんどの場合正しいですが、Firefox は配列インデックスの最適化において興味深い作業を行っており、実際のパフォーマンスが変数よりも優れている可能性があります。ただし、他のブラウザでの配列要素のパフォーマンス上の欠点を考慮すると、実際に Firefox のパフォーマンスのみを目的として開発する場合を除き、配列検索は避けるようにしてください。
また、プログラマーは、配列を深く掘り下げすぎないようにする必要があります。レベルが増えるほど、操作が遅くなるからです。
簡単に言えば、多くのネストされたレベルを持つ配列の操作は、配列要素の検索が遅いため遅くなります。 3 レベルにネストされた配列要素を操作する場合、1 つではなく 3 つの配列要素の検索を実行する必要があると想像してください。そのため、foo.bar を参照し続ける場合は、var bar = foo.bar を定義することでパフォーマンスを向上させることができます。
もう 1 つ非常に独断的なアドバイスがあります: for-in ループは使用しないでください。
この背後にあるロジックは非常に単純です。コレクション内の要素を反復するには、for-in ループの代わりに for ループまたは do-while ループを使用できます。配列項目もありますが、さらに時間が必要です。これらの要素を反復するために、JavaScript は要素ごとに関数を作成する必要があります。この関数ベースの反復では、一連のパフォーマンスの問題が発生します。追加の関数により、関数オブジェクトが作成および破棄されるコンテキストが導入されます。スコープ チェーン 追加の要素を先頭に追加します。
ループは何度も繰り返し実行されるため、パフォーマンスに関しては、ループ内で回避する必要がある作業が常に話題になります。したがって、パフォーマンスの最適化が必要な場合は、最初にループを操作することで最も明らかなパフォーマンスの向上が得られる可能性があります。
ループを最適化する 1 つの方法は、ループを定義するときに制御条件と制御変数を結合することです。これらを結合しない例を次に示します。 document.getElementById('foo').onclick = function(ev) { };
#1:检查 x 是否存在
#2:检查 x 是否小于 0 (译者注:我猜这里是作者的笔误)
#3:使 x 增加 1
然而如果你只是迭代元素中的一些元素,那么你可以使用while循环进行轮转来替代上面这种操作:
var x = 9; do { } while( x-- );
如果你想更深入地了解循环的性能,Zakas提供了一种高级的循环优化技巧,使用异步进行循环(碉堡了!)
8. 为HTML集合对象定义数组
JavaScript使用了大量的HTML集合对象,比如 document.forms,document.images 等等。通常他们被诸如 getElementsByTagName、getElementByClassName 等方法调用。
由于大量的DOM selection操作,HTML集合对象相当的慢,而且还会带来很多额外的问题。正如DOM标准中所定义的那样:“HTML集合是一个虚拟存在,意味着当底层文档被改变时,它们将自动更新。”这太可怕了!
尽管集合对象看起来跟数组很像,他们在某些地方却区别很大,比如对于特定查询的结果。当对象被访问进行读写时,查询需要重新执行来更新所有与对象相关的组分,比如 length。
HTML集合对象也非常的慢,Nicholas说好像在看球的时候对一个小动作进行60倍速慢放。另外,集合对象也有可能造成死循环,比如下面的例子:
var ps = document.getElementsByTagName('p'); for (var i=0; i < ps.length; i++ ) { var p = document.createElement("p"); document.appendChild(p); }
这段代码造成了死循环,因为 ps 表示一个实时的HTML集合,并不是你所期望的数组。这种实时的集合在添加
标签时被更新,所以i < p.length 永远都不会结束。
解决这个问题的方法是将这些元素定义成数组,相比只设置 var ps = document.getElementsByTagName(‘p') 稍微有点麻烦,下面是Zakas提供的强制使用数组的代码:
function array(items) { try { return Array.prototype.concat.call(items); } catch (ex) { var i = 0, len = items.length, result = Array(len); while (i < len) { result[i] = items[i]; i++; } return result; } } var ps = array( document.getElementsByTagName('p') ); for (var i=0l i < ps.length; i++ ) { var p = document.createElement("p"); document.appendChild(p); }
9. 不要碰DOM!
不使用DOM是JavaScript优化中另一个很大的话题。经典的例子是添加一系列的列表项:如果你把每个列表项分别加到DOM中,肯定会比一次性加入所有列表项到DOM中要慢。这是因为DOM操作开销很大。
Zakas对这个进行了细致的讲解,解释了由于回流(reflow)的存在,DOM操作是非常消耗资源的。回流通常被理解为浏览器重新选渲染DOM树的处理过程。比如说,如果你用JavaScript语句改变了一个p的宽度,浏览器需要重绘页面来适应变化。
任何时候只要有元素被添加到DOM树或者从DOM树移除,都会引发回流。使用一个非常方便的JavaScript对象可以解决这个问题——documentFragment,我并没有使用过,但是在Steve Souders也表示同意这种做法之后我感觉更加肯定了。
DocumentFragment 基本上是一种浏览器以非可视方式实现的类似文档的片段,非可视化的表现形式带来了很多优点,最主要的是你可以在 documentFragment 中添加任何结点而不会引起浏览器回流。
10. 修改CSS类,而不是样式
你也许听说过:修改CSS类必直接修改样式会更高效。这归结于回流带来的另一个问题:当布局样式发生改变时,会引发回流。
布局样式意味着任何影响改变布局的变化都会强制引起浏览器回流。比如宽度、高度、字号、浮动等。
但是别误会我的意思,CSS类并不会避免回流,但是可以将它的影响最小化。相比每次修改样式都会引起回流,使用CSS类一次修改多个样式,只需要承担一次回流带来的消耗。
因此在修改多个布局样式的时候,使用CSS类来优化性能是明智的选择。另外如果你需要在运行时定义很多歌CSS类,在DOM上添加样式结点也是不错的选择。
以上がJavaScript Web パフォーマンスを向上させる方法に関するヒントのまとめの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。