JavaScriptのパフォーマンスの最適化(パフォーマンスとメモリの管理)

記事の説明:この記事は、レガオのフロントエンドトレーニングキャンプのメモと経験です。何か問題がある場合は、指摘して教えていただければ幸いです。ありがとうございます。 

1.パフォーマンス

パフォーマンスを使用する理由

  • GCの目的は、メモリ空間の好循環を実現することです。
  • 好循環の要は合理的な使用です
  • それが合理的であるかどうかを判断するために常に注意を払ってください
  • パフォーマンスは複数の監視方法を提供します

パフォーマンスステップ

  • ブラウザを開き、リンク先URLを入力します
  • 開発者ツールパネルを開き、パフォーマンスを選択します
  • 録音機能をオンにして、特定のページにアクセスします
  • ユーザーの動作を実行し、一定期間後に記録してから記録を終了し、パフォーマンスボタンをクリックしてメモリ使用量を表示します

第二に、記憶の問題の発現

記憶の問題の外部症状:

  • ページの遅延読み込みまたは頻繁な一時停止(ネットワーク環境は正常です)
  • ページの永続的なパフォーマンスの低下
  • ページのパフォーマンスは時間の経過とともに悪化します

第三に、メモリを監視する一元化された方法

メモリの問題を定義するための標準

  • メモリリーク:メモリ使用量が増え続けている
  • メモリ拡張:ほとんどのデバイスでパフォーマンスの問題があります(現在のアプリケーション自体が最良の結果を達成するために多くのメモリスペースを必要とします。このプロセスでは、おそらく現在のデバイス自体がハードウェアをサポートしていません。使用中のパフォーマンス)
  • 頻繁なガベージコレクション:メモリ変更グラフによる分析

メモリを監視するいくつかの方法

  • ブラウザタスクマネージャー
  • タイムラインシーケンスチャートレコード
  • DOMを見つけて分離するためのヒープブロック写真
  • 面倒なガベージコレクションがあるかどうかを確認します

 1.タスクマネージャはメモリを監視します

ショートカットキーShift + Escを使用してブラウザのタスクマネージャを呼び出し、右クリックして現在のページのJavaScriptメモリを選択すると表示されます

最初の列が占めるメモリはネイティブメモリです。簡単に理解すると、現在のインターフェイスには多くのDomノードがあります。このメモリはDomが占めるメモリです。メモリが増え続ける場合は、ページが新しいドームを作成します。

JavaScriptで使用されるメモリの最後の列は、jsのヒープを示します。注意する必要があるのは、括弧内の値です。これは、到達可能なすべてのオブジェクトのサイズを示します。この数が増加している場合は、現在のいずれかの新しいオブジェクトがインターフェイスで作成された、または現在の既存のオブジェクトは絶えず成長しています。

問題があるかどうかしかわかりませんし、ポジショニングの問題は使いにくいです。

2.タイムライン記録メモリ

タイムラインを使用してメモリの変更を記録します。これにより、メモリの変更をより正確に監視できます。例:

 

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" charset="UTF-8" />
    <title>任务管理器监控内存变化</title> 
</head> 
<body>
    <button id="btn">
        Add
    </button>
<script type="text/javascript">
    const arrList = []

    function test(){
        for(let i = 0;i < 100000;i++){
            document.body.appendChild(document.createElement('p'))
        }
        arrList.push(new Array(100000).join('x'))
    }
    document.getElementById('btn').addEventListener('click',test)
</script> 
</body> 
</html>

Chromeブラウザでhtmlファイルを開き、コンソールを開いて、パフォーマンスオプションをクリックし、記録を開始してから、ページの[追加]ボタンをクリックし、断続的に数回クリックして、画面の記録を終了します。

 

 3.ヒープスナップショット検索と個別のDOM

個別のDOMとは

  • インターフェイス要素はDOMツリーに存在します
  • ガベージオブジェクトのDOMノード:このノードが現在のDOMツリーから切り離されていて、jsコード内に誰からも参照されているDOMノードがない場合、実際にはガベージになります。
  • 分離状態のDOMノード:このノードが現在のDOMツリーから切り離されており、jsコード内の誰かによって参照されているDOMノードがある場合、それは分離状態のDOMノードと呼ばれます。

例:ボタンをクリックすると、DOMツリーに分離されたDOMが生成されます。分離されたDOMはページでは使用されませんが、コードで使用されるため、スペースが無駄になります。TmpEleを空のままにして、GCに収集させます。ごみ。

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" charset="UTF-8" />
    <title>任务管理器监控内存变化</title> 
</head> 
<body>
    <button id="btn">
        Add
    </button>
<script type="text/javascript">
    var tmpEle

    function  fn() {
        var ul = document.createElement('ul')
        for(var i = 0; i<10 ;i++ ){
            var li = document.createElement('li')
            ul.appendChild(li)
        }
        tmpEle = ul
    }

    document.getElementById('btn').addEventListener('click',fn)//js中创建的节点并没有往我们页面里添加,是分离的DOM
</script> 
</body> 
</html>

ここに画像の説明を挿入

 上記のアニメーションのソース:https//coder5.blog.csdn.net/article/details/109723747

4.GCが頻繁に発生するかどうかを判断します

頻繁なガベージコレクションを決定する理由

  • GCが動作している間、アプリケーションは停止します
  • 頻繁で長いGCは、アプリケーションを停止させます
  • ユーザーは、使用中にアプリケーションがフリーズすることを認識します

頻繁なガベージコレクションを決定する

  • タイムラインの頻繁な上昇と下降
  • タスクマネージャでのデータの頻繁な増減

4、コード最適化の紹介

JavaScriptのパフォーマンスを正確にテストする方法

  • 基本的に、数理統計と分析のために多数の実行サンプルを収集します
  • Benchmark.jsに基づくhttps://jsperf.comを使用します(サーバーはメンテナンスされておらず、使用されていません。JSBench:https://jsbench.me/を使用できます)。

Jsperfの使用プロセス

  • GitHubアカウントでログインします
  • 個人情報の入力(必須ではありません)
  • 詳細なテストケース情報(タイトル、スラッグ)を入力します:スラッグは一意である必要があります
  • 準備コードを入力します(通常はDOM操作で使用されます)
  • 必要なセットアップコードとティアダウンコードを入力します。セットアップは事前準備であり、ティアダウンはすべてのコードが実行された後に破棄される作業です。
  • テストコードスニペットを入力します
  • ここに画像の説明を挿入

5、グローバル変数を注意して使用する

注意を払う理由

  • グローバル変数はグローバル実行コンテキストで定義され、すべてのスコープチェーンの最上位にあります
  • グローバル実行コンテキストは常にコンテキスト実行スタックに存在し、プログラムが終了するまで消えません。これは、GCがグローバル変数を再利用しないため、GCの動作状態には適していません。変数はまだ生きています。
  • 同じ名前の変数がローカルスコープに表示されると、グローバルスコープが不明瞭になったり汚染されたりします

コードデモ:

var i,str = ''
for(i = 0;i<1000;i++){
    str += i
}

for(i = 0;i<1000;i++){
    let str = '' //函数内部定义的局部变量
    str += i
}

 https://jsperf.comデモ:

 

 グローバル変数で定義されたiのコードのパフォーマンスは、ローカル変数で定義されたものほど良くないことがわかります。

グローバル変数をキャッシュします。

使用中の避けられないグローバル変数をローカルにキャッシュします。DOM要素を検索する場合は、この場合、ドキュメントを使用する必要があります。このドキュメントは後で定義されませんが、現在のグローバルに存在します。オブジェクトは直接組み込みであり、が使用されるため、この場合、キャッシュの効果を実現するために、ローカルアクションで再利用する必要がある非常に多くのグローバル変数を配置することを選択できます。

コードデモ:

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" charset="UTF-8" />
    <title>缓存全局变量</title> 
</head> 
<body>
    <input type="button" value="btn" id="btn1">
    <input type="button" value="btn" id="btn2">
    <input type="button" value="btn" id="btn3">
    <input type="button" value="btn" id="btn4">
    <p>1111</p>
    <input type="button" value="btn" id="btn5">
    <input type="button" value="btn" id="btn6">
    <p>222</p>
    <input type="button" value="btn" id="btn7">
    <input type="button" value="btn" id="btn8">
    <p>333</p>
    <input type="button" value="btn" id="btn9">
    <input type="button" value="btn" id="btn10">
<script type="text/javascript">
    function getBtn(){
        let oBtn1 = document.getElementById('btn1')
        let oBtn3 = document.getElementById('btn3')
        let oBtn5 = document.getElementById('btn5')
        let oBtn7 = document.getElementById('btn7')
        let oBtn9 = document.getElementById('btn9')
    }

    function getBtn2(){
        let obj = document
        let oBtn1 = obj.getElementById('btn1')
        let oBtn3 = obj.getElementById('btn3')
        let oBtn5 = obj.getElementById('btn5')
        let oBtn7 = obj.getElementById('btn7')
        let oBtn9 = obj.getElementById('btn9')
    }
</script> 
</body> 
</html>

本文以外のコンテンツなしで、本文のhtmlコードを準備フェーズに入れます

グローバル変数をキャッシュするコードのパフォーマンスが向上

プロトタイプオブジェクトを介してメソッドを追加する:インスタンスオブジェクトに必要なメソッドをプロトタイプオブジェクトに追加します。コードは次のように示されます。

var fn1 = function(){
    this.foo = function(){//相当于在某个构造函数内部直接给所有的对象实例添加上了成员方法,后续再使用的过程中都可以拿到
        console.log(11111)
    }
}
let f1 = new fn1()

var fn2 = function(){}
fn2.prototype.foo = function(){//通过原型对象添加方法
    console.log(11111)
}

let f2 = new fn2()

 

 プロトタイプオブジェクトを介してメソッドを追加することをお勧めします。

 

おすすめ

転載: blog.csdn.net/weixin_41962912/article/details/110135353