web-dev-qa-db-ja.com

ラムダ関数とバインド、メモリ! (およびパフォーマンス)

同等のソリューション間のベストプラクティスを決定したいと思います。ユースケースは、イベントをリッスンするクラスのインスタンスです。アクセル・ラウシュマイヤー博士 読みやすいように、ラムダが望ましい 。私は彼に同意します。しかし、パフォーマンスとメモリ消費の観点から、どちらが最適ですか?

ラムダ関数を使用する

class Abc {
  constructor() {
    let el = document.getElementById("my-btn")
    if (el)
      el.addEventListener("click", evt => this.onClick(evt))
  }
  onClick(evt) {
    console.log("Clicked!", evt.target)
  }
}

ローカル変数(ここではel)がガベージコレクターによってクリアできない場合、誰かが確認または確認できますか?または、最新のブラウザーはクロージャーで使用されていないことを検出できますか?

Function.prototype.bind

class Abc {
  constructor() {
    let el = document.getElementById("my-btn")
    if (el)
      el.addEventListener("click", this.onClick.bind(this))
  }
  onClick(evt) {
    console.log("Clicked!", evt.target)
  }
}

メモリの問題はありませんが、すべてのベンチマークは、bindがクロージャーよりもはるかに遅いことを示唆しています(例 here )。

編集:bindのパフォーマンスの問題を無視するコメントに同意しません。 Chromeの実装コードで この回答 を読むことをお勧めします。効率的ではありません。そして、私は主張します:all私が見たベンチマークは、allブラウザー

低いメモリ使用量と優れたパフォーマンスを同時に実現する方法はありますか?

21
Paleo

クロージャ(または矢印関数、別名lambdas)はメモリリークを引き起こしません

ローカル変数(ここではel)がガベージコレクターによってクリアできない場合、誰かが確認または確認できますか?または、最新のブラウザーはクロージャーで使用されていないことを検出できますか?

はい、最新のJavaScriptエンジンは、クロージャーからは見えるが未使用の親スコープから変数を検出できます。私はそれを証明する方法を見つけました。

ステップ1:クロージャーは10 MBの変数を使用します

Chromiumでこのコードを使用しました。

_class Abc {
    constructor() {
        let arr = new Uint8Array(1024*1024*10) // 10 MB
        let el = document.getElementById("my-btn")
        if (el)
            el.addEventListener("click", ev => this.onClick(ev, arr))
    }
    onClick(ev) {
        console.log("Clicked!", ev.target)
    }
}

new Abc()
_

タイプ_Uint8Array_の変数arrに注意してください。これは、サイズが10メガバイトの 型付き配列 です。この最初のバージョンでは、変数arrがクロージャーで使用されます。

次に、Chromiumの開発者ツールの[プロファイル]タブで、ヒープスナップショットを取得します

Snapshot 1: the variable <code>arr</code> is used in the closure

サイズを小さくして並べ替えると、最初の行は次のようになります: "system/JSArrayBufferData"サイズが10 MB。変数arrです。

ステップ2:10 MBの変数は表示されますが、クロージャーでは使用されません

次のコード行でarrパラメーターを削除します。

_            el.addEventListener("click", ev => this.onClick(ev))
_

次に、2番目のスナップショット:

Snapshot 2: the variable <code>arr</code> is not used in the closure

最初の行は消えました。

この経験により、ガベージコレクターは、アクティブクロージャーでは表示されているが使用されていない親スコープから変数を削除できることを確認します。

_Function.prototype.bind_について

Google JavaScript Style Guide 、矢印関数のセクションを引用します:

f.bind(this)またはgoog.bind(f, this)を呼び出さないでください(_const self = this_の記述は避けてください)。これらはすべて、矢印関数を使用してより明確に表現でき、エラーが発生しにくくなります。これは、予期しない追加の引数を渡すことがあるコールバックの場合に特に便利です。

Googleは、_Function.prototype.bind_ではなくラムダを使用することを明確に推奨しています。

関連:


19
Paleo