ホームページ  >  記事  >  ウェブフロントエンド  >  Web パフォーマンスの最適化 - JavaScript パフォーマンスチューニング_JavaScript スキル

Web パフォーマンスの最適化 - JavaScript パフォーマンスチューニング_JavaScript スキル

WBOY
WBOYオリジナル
2016-05-16 17:44:56886ブラウズ

JavaScript は比較的完成度の高いフロントエンド開発言語であり、今日の Web 開発、特に Web 2.0 アプリケーションで広く使用されています。今日、Web 2.0 の人気がますます高まっているため、Web アプリケーション プロジェクトには大量の JavaScript コードが含まれるようになり、将来的にはさらに多くなるでしょう。 JavaScript は、解釈および実行される言語、およびそのシングルスレッド メカニズムとして、パフォーマンスが JavaScript の弱点であると判断されており、これは、Web ソフトウェア エンジニアが、特に Web 2.0 アプリケーションの場合に JavaScript を作成する際に細心の注意を払う必要がある問題でもあります。 Web ソフトウェア エンジニアの大多数は、多かれ少なかれ、開発した Web 2.0 アプリケーションのパフォーマンスが低下するという問題に遭遇したことがあります。主な原因は、不十分な JavaScript パフォーマンスと過負荷のブラウザです。ただし、このようなインタープリタ型のシングルスレッド言語のパフォーマンスの問題を解決するのは簡単ではありません。この記事では、開発中の JavaScript パフォーマンス チューニングのヒントとベスト プラクティスに焦点を当て、JavaScript 操作 DOM ノードのパフォーマンス チューニングのいくつかの方法についても説明します。

概要
パフォーマンスの問題についても説明します。 Web 開発、特に今日の Web2.0 アプリケーションで頻繁に遭遇する問題です。 JavaScript は、現在最も広く使用されている Web 開発言語です。Web アプリケーションのパフォーマンスの問題の大部分は、JavaScript 言語自体のパフォーマンスの問題や DOM のパフォーマンスの問題など、プログラマが作成した JavaScript スクリプトのパフォーマンスの低下によって引き起こされます。問題。この記事では主に、このような問題をできるだけ回避し、Web アプリケーションのパフォーマンスを最大化する方法について説明します。

JavaScript のパフォーマンス チューニング
JavaScript 言語にはシングルスレッドでインタープリタ型の実行特性があるため、パフォーマンス上の問題が多く、改善の余地があります。

eval の問題:
次のコードを比較してください:
リスト 1. eval の問題

コードをコピー コードは次のとおりです。

varreference = {}, props = “p1”
eval(“reference.” props “=5”; )
varreference = {}, props = “p1”;
reference[props] = 5

「eval」を使用したコードは、「eval」を使用しないコードよりも 100 倍以上遅くなります” 。
主な理由は次のとおりです。JavaScript コードは、実行前に同様の「プリコンパイル」操作を実行します。まず、現在の実行環境でアクティブ オブジェクトを作成し、var で宣言された変数をアクティブ オブジェクトの属性として設定します。ただし、この時点では、これらの変数の割り当てはすべて未定義であり、 function で定義された関数もアクティブ オブジェクトの属性として追加され、それらの値は関数の定義とまったく同じになります。ただし、「eval」を使用すると、「eval」内のコード (実際には文字列) は事前にコンテキストを認識できず、事前に解析および最適化することができません。つまり、プリコンパイルされた操作を実行できません。したがって、そのパフォーマンスは大幅に低下します。

関数の使用法:
次のコードを比較します:
リスト 2. 関数の使用法
コードをコピーします コードは次のとおりです。

var func1 = new Function(“return argument[0] argument[1]”);
func1 (10, 20);
var func2 = function(){ return argument[0] argument[1] };

同様ここでの前提 「eval」メソッドの場合、ここでの「func1」の効率は「func2」の効率よりもはるかに悪いため、2番目のメソッドを使用することをお勧めします。


関数のスコープ チェーン: JavaScript コードは関数に入るときに解釈されて実行され、現在の変数が事前に分析され、これらの変数がさまざまなレベル (レベル) に分類されます。 )、一般的に言えば、
ローカル変数はレベル 1 (浅い) に配置され、グローバル変数はレベル 2 (深い) に配置されます。 「with」または「try - catch」コード ブロックを入力すると、新しいレベルが追加されます。つまり、「with」または「catch」の変数は最も浅いレベル (レベル 1) に配置され、以前のレベルは順番に深められます。
次のコードを参照してください。

リスト 3. 関数スコープ チェーン
コードをコピーコードは次のとおりです:
var myObj = … ..
… ..
function process(){
var image = document.getElementsByTagName("img"),
widget = document.getElementsByTagName("input"),
combination = [];
for(var i = 0; i combination.push(combine(images) [i ], widget[2*i]));
}
myObj.container.property1 = 組み合わせ[0];
myObj.container.property2 = 組み合わせ[combination.length-1]; 🎜> }


ここでは、「画像」、「ウィジェット」、および「組み合わせ」がレイヤー 1 のローカル変数に属していることがわかります。 「document」、「myObj」はレイヤー 2 のグローバル変数に属します。
変数が配置されている層が浅いほどアクセス (読み取りまたは変更) 速度は速くなり、深い層ほどアクセス速度は遅くなります。したがって、ここでの「画像」、「ウィジェット」、「組み合わせ」へのアクセス速度は、「ドキュメント」や「myObj」よりも高速です。したがって、次のコードに示すように、できる限りローカル変数を使用することをお勧めします。
リスト 4. ローカル変数の使用
コードをコピー コードは次のとおりです:

var myObj = … ..
… ..
function process(){
var doc = ドキュメント;
var イメージ = doc.getElementsByTagName("img")、
ウィジェット = doc.getElementsByTagName("input")、
組み合わせ = []; 0; i combination(images[i], widget[2*i]));
myObj.container.property1 = 0];
myObj.container.property2 = combination.length-1];
}


グローバル変数 "document" の代わりにローカル変数 "doc" を使用します。特に多数のグローバル変数を使用する関数のパフォーマンスを向上させることができます。
もう一度次のコードを見てください。

リスト 5.

コードをコピーしてください コードは次のとおりです。
var myObj = … ..
… ..
function process(){
var doc = document; .getElementsByTagName(" img"),
widget = doc.getElementsByTagName("input"),
combination = [];
for(var i = 0; i 組み合わせ .push(combine(images[i], widget[2*i]));
}
with (myObj.container) {
property1 =組み合わせ
; property2 = combo[combination.length-1];
}
}


「with」キーワードを使用すると、コードがより簡潔かつ明確になりますが、パフォーマンスに影響します。 。前述したように、「with」コードブロックに入ると、元のレイヤー1からレイヤー2に「組み合わせ」が変わるため、効率が大幅に低下します。したがって、比較のために、引き続き元のコードを使用します。
リスト 6.


コードをコピー による改善コードは次のとおりです。 var myObj = … ..
… ..
function process(){
var doc = document;
var image = doc; .getElementsByTagName( "img"),
widget = doc.getElementsByTagName("input"),
combination = [];
for(var i = 0; i combine.push(combine(images[i], widget[2*i]));
}
myObj.container.property1 = 組み合わせ[0]; combination[combination .length-1];
}


しかし、JavaScript にはオブジェクト オブジェクトの属性アクセス レベルが深くなるほど効率が低下するという特性があります。たとえば、ここでは「myObj」が 3 番目の層にアクセスしています。

リスト 7. オブジェクトのアクセス レベルを下げる



コードをコピーします
コードは次のとおりです: var myObj = … .. … ..
function process(){
var doc = document ;
var image = doc.getElementsByTagName("img"),
widget = doc.getElementsByTagName("input"),
combination = []
for( var i = 0; i
combination.push(images[i], widget[2*i]));
var ctn = myObjコンテナ;
ctn.property1 = combination[0];
ctn.property2 = combination.length-1];


「myObj」「container」オブジェクトの 2 番目の層。オブジェクトの深いプロパティへのこのようなアクセスが多数ある場合は、上記のメソッドを参照してパフォーマンスを向上させることができます。


文字列関連
文字列の結合
よく次のようなコードを目にします。

リスト 8. 単純な文字列の結合


コードをコピーします
コードは次のとおりです: str = “str1” “str2”
これは文字列を結合する一般的な方法ですが、この方法では一部の一時変数が作成および破棄され、パフォーマンスに影響するため、次のメソッドを使用して結合することをお勧めします。
リスト 9. 文字列配列メソッド Splice
コードをコピー コードは次のとおりです。

var str_array = [ ];
str_array.push("str1");
str = str_array.join(""); use array (array) の "join" メソッドは文字列のスプライシングを実装します。特に、古いバージョンの Internet Explorer (IE6) で実行する場合、非常に明らかなパフォーマンスの向上が見られます。
もちろん、最新のブラウザー (Firefox3、IE8 など) では文字列のスプライシングが最適化されており、次のように記述することもできます。
リスト 10. クイック文字列のスプライシング



コードをコピーします
コードは次のとおりです: str = "str1" str = "str2"

新しいブラウザは "= を最適化しており、配列の "結合" メソッドよりもパフォーマンスがわずかに高速です。近い将来、ブラウザの更新バージョンでも " " が最適化される可能性があるため、str = "str1" "str2" と直接書くことができます。

暗黙的な型変換

次のコードを参照してください:
リスト 11. 暗黙的な型変換


コードをコピー
コードは次のとおりです。 var str = “12345678”, arr = for(var i = 0; i arr.push( str.charAt(i));


ここで各ループで文字列を呼び出します「charAt」メソッドですが、「str」に定数「12345678」を代入しているため、ここでは「str」は実際には文字列オブジェクトではなく、「charAt」関数を呼び出すたびに一時的に値を構築します。 「12345678」の文字列オブジェクトを取得し、「charAt」メソッドを呼び出し、最後に文字列一時オブジェクトを解放します。

リスト 12. 暗黙的な型変換の回避


コードをコピー
Code As var str = new Stirng(“12345678”), arr = []; for(var i = 0; i arr.push( str.charAt(i));
}


このように、文字列オブジェクトとしての変数「str」には、この暗黙の型変換処理がありません。このようにして、効率が大幅に向上します。


文字列マッチング

JavaScript には、文字列の正規表現マッチングをサポートする RegExp オブジェクトがあります。素晴らしいツールですが、パフォーマンスはあまり良くありません。それどころか、「substring」、「indexOf」、「charAt」などの文字列オブジェクト (String) 自体のいくつかの基本メソッドは非常に効率的です。文字列を照合するために正規表現を使用する必要がある場合は、それを検討できます。 . :
文字列オブジェクト自体がサポートする基本的なメソッドで問題を解決することは可能ですか。
「部分文字列」を使用して正規表現の範囲を狭めることはできますか? これらの方法は、プログラムの効率を効果的に向上させることができます。 正規表現オブジェクトについては、もう 1 つ注意すべき点があります。次のコードを参照してください。

リスト 13. 正規表現



コードをコピー
コードは次のとおりです: for(var i = 0; i if (str_array[i].match(/^s*extras/)){ ……………………
}
}


ここで、 を渡します「 /^s*extras/」は効率に影響します。「/^s*extras/」の一時的な値を使用して正規表現オブジェクトを構築し、「match」メソッドを実行してから、一時的な正規表現オブジェクトを破棄します。

リスト 14. 変数の使用


コードをコピーします
コードは次のとおりです。 var sExpr = /^s*extras/; for(var i = 0; i if(str_array[i ].match( sExpr)){
……………………
}
}


この方法では、一時的なオブジェクトは存在しません。
setTimeout と setInterval
2 つの関数「setTimeout」と「setInterval」は文字列変数を受け入れることができますが、前述の「eval」と同様のパフォーマンスの問題が発生するため、これらを直接渡すことをお勧めします。オブジェクトそのもの。

早期終了の使用
次の 2 つのコードを参照してください。
リスト 15. 早期終了の利用
コードをコピー コードは次のとおりです:

// コード 1
var name = … .;
var ソース = … ;
if(source.match(/ …… /)){
…………………………
}
//コード 2
var name = … .;
var source = …… ;
if(name.indexOf( … ) &&source.match(/ …… /)){
……… ………………………
}

コード 2 には、「name.indexOf(...)」に関する追加の判断があり、プログラムは最初に「indexOf」を実行します。 「このセクションに到達するたびに判定を行い、その後の match を実行します。これは、「indexOf」が「match」よりもはるかに効率的であることを前提としています。これにより、「match」の実行回数が減り、効率が向上します。ある程度。
----------------------------------------------- --- ----------------------------------
DOM 操作パフォーマンスのチューニング
JavaScript の開発は DOM 操作と切り離せないため、Web 開発では DOM 操作のパフォーマンス チューニングも非常に重要です。
再ペイントとリフロー
再ペイントは再描画とも呼ばれ、現在の DOM の構造とレイアウトに影響を与えない再描画アクションを指します。次のアクションは再描画アクションを生成します:
不可視から可視へ (可視性スタイル属性)
色または画像の変更 (背景、境界線の色、色のスタイル属性)
サイズ、形状、位置は変更しません。ただし、外観を変える変更
リフローは、再ペイントよりも重要な変更です。これは主に、DOM ツリーが操作されるときに発生し、DOM の構造とレイアウトが変更されるとリフローが発生します。ただし、要素の Reflow 操作が発生すると、その親要素と子要素がすべて Reflow を解放し、最終的に Reflow により Repaint が生成されます。たとえば、次のアクションは再描画アクションを生成します:

ブラウザ ウィンドウの変更
DOM ノードの追加および削除操作
サイズ、形状、およびサイズを変更するいくつかの操作ページ要素の位置 トリガー
リフローの削減
リフローと再ペイントの導入から、各リフローはその再ペイントよりも多くのリソースを消費することがわかります。リフローの発生を減らすか、リフローのみに変換する必要があります。トリガーの再描画操作のコード。
次のコードを参照してください:
リスト 16. リフローの概要
コードをコピーコードは次のとおりです:

var pDiv = document.createElement(“div”);
document.body.appendChild(pDiv);----- reflow
var cDiv1 = document.createElement(" div");
var cDiv2 = document.createElement("div");
pDiv.appendChild(cDiv1);----- リフロー
pDiv.appendChild(cDiv2); ----- リフロー

これは私たちがよく目にするコードですが、このコードは 3 つのリフローを生成します。次のコードをもう一度見てください。
リスト 17. リフローの削減

var pDiv = document.createElement(“div”);
var cDiv1 = document.createElement(“div”);
var cDiv2 = document.createElement( "div");
pDiv.appendChild(cDiv2);
document.body.appendChild(pDiv);----- リフロー

🎜>ここでのリフローは 1 回だけなので、この方法で DOM ノードを操作することをお勧めします。
リフロー操作が少ない上記の解決策に関しては、参照できる別のパターンがあります。

リスト 18. ディスプレイを使用してリフローを削減する

コードをコピーします コードは次のとおりです。
var pDiv = document.getElementById(“parent”);
pDiv.style.display = “なし”- ---- リフロー
pDiv.appendChild(cDiv1);
pDiv.appendChild(cDiv3);
pDiv.appendChild(cDiv5);
pDiv.style.width = “100px”;
pDiv.style.display = “ブロック” --- リフロー


最初に pDiv を非表示にしてから表示することで、非表示と表示の間の操作でリフローが発生せず、効率が向上します。

特別な測定属性とメソッド
DOM 要素には、リフローをトリガーする特別な測定属性へのアクセスとメソッド呼び出しがいくつかあります。代表的なものは、「offsetWidth」属性と「getComputedStyle」メソッドです。
図 1. 特別な測定属性とメソッド
Web パフォーマンスの最適化 - JavaScript パフォーマンスチューニング_JavaScript スキル
これらの測定属性とメソッドは、おおよそ次のとおりです:
コードをコピー コードは次のとおりです。

offsetLeft
offsetTop
offsetHeight
offsetWidth
scrollTop/Left/Width/Height
clientTop/Left /Width/Height
getComputedStyle()
currentStyle(IE))

これらのプロパティとメソッドへのアクセスと呼び出しにより、リフローの生成がトリガーされます。これらのプロパティへのアクセスは最小限に抑える必要があります。
リスト 19. 特別な測定属性
コードをコピーします コードは次のとおりです:

var pe = document.getElementById(“pos_element”);
var result = document.getElementById(“result_element”); = pe.offsetWidth;
result.children[0].style.width = pOffsetWidth;
result.children[1].style.width = pOffsetWidth; = pOffsetWidth;


…………その他の変更…………
ここでは、一時変数を使用して「offsetWidth」の値をキャッシュすることができます。毎回「offsetWidth」属性。この方法はループに非常に適しており、パフォーマンスを大幅に向上させることができます。


スタイル関連
よく次のコードを目にします。
リスト 20. スタイル関連

コードをコピーします コードは次のとおりです。
var sElement = document.getElementById("pos_element"); .border = ' 1px 赤一色 '
sElement.style.backgroundColor = ' シルバー '
sElement.style.padding = ' 2px 3px '
sElement.style.marginLeft = ' 5px '


しかし、ここでスタイルを変更するたびにリフローが発生することがわかります。この状況の発生を減らすには、次のようにします。
解決策 1
:
リスト 21. className Solution

コードをコピー コードは次のとおりです: .class1 {
border: ' 1px 単色赤 '
background-color: ' シルバー '
パディング: ' 2px 3px '
マージン左: ' 5px '
}
document.getElementById("pos_element").className = 'class1' ;
スタイルの代わりにクラスを使用すると、元のリフローまたは再ペイントの数を 1 つに減らすことができます。


解決策 2
:

リスト 22. cssText ソリューション

コードをコピー
コードは次のとおりです: var sElement = document.getElementById(“pos_element”); var newStyle = ' border: ' ' 背景色; : silver; ' 'padding: 2px 3px; ' "margin-left: 5px;"
sElement.style.cssText = newStyle;リフロー方法を減らしてパフォーマンスを向上させます。
XPath
ページには 1,000 を超えるページ要素が含まれることが多く、特定の要素を見つけるのにある程度の時間がかかることがよくあります。 ID または名前を使用して検索する場合、効率はそれほど遅くない可能性があります。要素の他の属性 (className など) を使用して検索する場合、効率は理想的ではない可能性があります。すべての要素 (getElementsByTagName) を走査してフィルタリングすることによってのみ対応する要素を見つけることができる場合もありますが、これはさらに非効率です。ここでは、多くのブラウザでサポートされている機能である XPath を使用して要素を見つけることをお勧めします。

リスト 23. XPath ソリューション




コードをコピーします
コードは次のとおりです。

if(document.evaluate){
var tblHeaders = document.evaluate(“//body/div/table//th”);
var result = tblHeaders.iterateNext(); 🎜>while(result) {
result.style.border = “1px 点線青”;
result ………………
result = xpathResult.iterateNext();
} else{ //getElementsByTagName() ……
// ブラウザが XPath をサポートしていない場合の状況を処理します
………………………………
}


ブラウザ XPath の検索エンジンは、検索効率を最適化し、結果が返される時間を大幅に短縮します。


HTMLCollection オブジェクト
これは特殊なタイプのオブジェクトで、配列に少し似ていますが、正確には配列ではありません。次のメソッドの戻り値は通常、HTMLCollection オブジェクトです: document.images、document.forms
getElementsByTagName()
getElementsByClassName()
これらの HTMLCollection オブジェクトは固定値ではなく、動的な結果。これらは一部の特殊なクエリの戻り値です。次の場合、前のクエリが再実行され、新しい戻り値 (クエリ結果) が得られますが、ほとんどの場合、それらは前のクエリと同じになります。または複数の戻り値:


長さ属性
特定のメンバー したがって、HTMLCollection オブジェクトによるこれらの属性およびメンバーへのアクセスは、配列のアクセスよりもはるかに遅くなります。もちろん例外もありますが、Opera と Safari は、パフォーマンスに大きな問題を引き起こすことなく、この状況をうまく処理します。
次のコードを参照してください。

リスト 24. HTMLConnection オブジェクト

var items = [“test1”, “test2”, “test3”, ……………];
for(var i = 0; i < items.length; i ){
…………………………
}
var items = document.getElementsByTagName(“div”);
for(var i = 0) ; i < items.length; i ){
……………………………… .


上記の方法は、各サイクルで "items.length" がトリガーされ、"document.getElementsByTagName(..)" メソッドが再度呼び出されるため、上記のものよりもはるかに効率的です。大幅に下がります。これは次のように解決できます。
リスト 25. HTMLConnection オブジェクト ソリューション


コードをコピー コード var items = document.getElementsByTagName(“div”);
var len = items.length
for(var i = 0; i < len; i ){
………………………………………… .
}


このように、効率は基本的に普通の配列。
スクリプトタグを動的に作成する
JavaScript スクリプトのロードと実行には一定の時間がかかります。プログラムでは、一部の JavaScript スクリプトがロード後に基本的に使用されないことがあります (例: スクリプト内の関数がなどと呼ばれて使用されたことはありません)。これらのスクリプトを読み込むと、CPU 時間が消費され、メモリ消費量が増加するだけで、Web アプリケーションのパフォーマンスが低下します。したがって、JavaScript スクリプト ファイル、特にコンテンツが大きくリソース消費量が多いファイルを動的にロードすることをお勧めします。

リスト 26. スクリプト タグの作成


コードをコピーします コードは次のとおりです。 if(needXHR){
document.write(“