ホームページ  >  記事  >  ウェブフロントエンド  >  高性能JavaScript DOMプログラミング(1)_javascriptスキル

高性能JavaScript DOMプログラミング(1)_javascriptスキル

WBOY
WBOYオリジナル
2016-05-16 15:45:511129ブラウズ

DOM は XML および HTML ドキュメントを操作するためのアプリケーション プログラミング インターフェイスであり、DOM を操作するためにスクリプトを使用すると非常にコストがかかることはわかっています。 DOM と JavaScript (ここでは ECMScript) がそれぞれ 1 つの島であり、ECMAScript が DOM にアクセスするたびに、この橋を通過して「橋の通行料」を支払う必要があると想像してください。 DOM へのアクセス回数が増えるほど、コストが高くなります。したがって、推奨されるアプローチは、できるだけ少ない数の橋を渡り、ECMAScript アイランドにとどまるようにすることです。 DOM インターフェースを使用しないことは不可能ですが、プログラムの効率を向上するにはどうすればよいでしょうか?

1. DOM へのアクセスと変更
DOM 要素へのアクセスにはコストがかかります (ご存知のとおり、「料金」がかかります)。また、要素の変更には、ブラウザがページの幾何学的変更 (リフローと再描画) を再計算する必要があるため、さらにコストがかかります。

もちろん、最悪のシナリオはループ内の要素にアクセスまたは変更することです。次の 2 つのコードを見てください:

var times = 15000;

// code1
console.time(1);
for(var i = 0; i < times; i++) {
 document.getElementById('myDiv1').innerHTML += 'a';
}
console.timeEnd(1);

// code2
console.time(2);
var str = '';
for(var i = 0; i < times; i++) {
 str += 'a';
}
document.getElementById('myDiv2').innerHTML = str;
console.timeEnd(2);

その結果、最初の実行時間は 2 回目の実行時間よりも 1,000 倍長くなりました。 (クロームバージョン44.0.2403.130m)

1: 2846.700ms
2: 1.046ms

コードの最初の部分の問題は、各ループ反復で要素が 2 回アクセスされることです。1 回目は innerHTML の値を読み取るため、もう 1 回目は値を書き換えるためです。つまり、ループがブリッジを通過するたびに ( re-Rowingとredrawについては次の記事で説明します)!この結果は、DOM へのアクセス回数が増えるほど、コードの実行が遅くなることを明確に示しています。そのため、削減できるDOMアクセス数を極力減らし、ECMAScript側に処理を任せることになります。

2. HTML コレクションと DOM の走査
DOM の操作でエネルギーを消費するもう 1 つのポイントは、DOM の走査です。一般に、getElementsByTagName() や document.links などを使用して HTML のコレクションを収集します。これについては誰もがよく知っていると思います。コレクションの結果は、リアルタイムで「ライブ状態」で存在する配列のようなコレクションです。つまり、基になるドキュメント オブジェクトが更新されると自動的に更新されます。どう言えばいいでしょうか?栗をあげるのはとても簡単です:

<body>
 <ul id='fruit'>
 <li> apple </li>
 <li> orange </li>
 <li> banana </li>
 </ul>
</body>
<script type="text/javascript">
 var lis = document.getElementsByTagName('li');
 var peach = document.createElement('li');
 peach.innerHTML = 'peach';
 document.getElementById('fruit').appendChild(peach);

 console.log(lis.length); // 4
</script>

そして、これが非効率の原因です。配列の最適化操作と同じように、長さ変数をキャッシュしても問題ありません (コレクションの長さの読み取りは、毎回クエリを実行する必要があるため、通常の配列の長さを読み取るよりもはるかに時間がかかります)。 >

console.time(0);
var lis0 = document.getElementsByTagName('li');
var str0 = '';
for(var i = 0; i < lis0.length; i++) {
 str0 += lis0[i].innerHTML;
}
console.timeEnd(0);


console.time(1);
var lis1 = document.getElementsByTagName('li');
var str1 = '';
for(var i = 0, len = lis1.length; i < len; i++) {
 str1 += lis1[i].innerHTML;
}
console.timeEnd(1);

どの程度パフォーマンスが向上するか見てみましょう?

0: 0.974ms
1: 0.664ms
コレクションの長さが長い場合 (デモが 1000)、パフォーマンスの向上は明らかです。

「High-Performance JavaScript」では、別の最適化戦略を提案しています。「配列の走査はコレクションの走査よりも速いため、最初にコレクションの要素を配列にコピーすると、そのプロパティへのアクセスがより速くなります。」と述べています。このパターンはあまりよく分かりませんでしたので、気にしないでください。テスト コードは次のとおりです。 (ご質問がございましたら、お気軽にご相談ください)

console.time(1);
var lis1 = document.getElementsByTagName('li');
var str1 = '';
for(var i = 0, len = lis1.length; i < len; i++) {
 str1 += lis1[i].innerHTML;
}
console.timeEnd(1);


console.time(2);
var lis2 = document.getElementsByTagName('li');
var a = [];
for(var i = 0, len = lis2.length; i < len; i++)
 a[i] = lis2[i];

var str2 = '';
for(var i = 0, len = a.length; i < len; i++) {
 str2 += a[i].innerHTML;
}
console.timeEnd(2);

このセクションの最後では、querySelector() と querySelectorAll() という 2 つのネイティブ DOM メソッドを紹介します。前者は配列を返します (戻り値は動的に変化しないことに注意してください)。 HTML コレクションと同様)、後者の or は最初に一致した要素を返します。まあ、常に前者の HTML コレクション走査よりもパフォーマンスが優れているわけではありません。

console.time(1);
var lis1 = document.getElementsByTagName('li');
console.timeEnd(1);

console.time(2);
var lis2 = document.querySelectorAll('li');
console.timeEnd(2);

// 1: 0.038ms
// 2: 3.957ms
ただし、CSS に似た選択方法であるため、組み合わせ選択を行う場合はより効率的で便利です。たとえば、次の複合クエリを実行します:

var elements = document.querySelectorAll('#menu a');
var elements = document.querySelectorAll('div.warning, div.notice');
上記はすべて、高パフォーマンスの JavaScript DOM プログラミングに関するものです。ご理解いただき、学習に役立つことを願っています。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。