同等のソリューション間のベストプラクティスを決定したいと思います。ユースケースは、イベントをリッスンするクラスのインスタンスです。アクセル・ラウシュマイヤー博士 読みやすいように、ラムダが望ましい 。私は彼に同意します。しかし、パフォーマンスとメモリ消費の観点から、どちらが最適ですか?
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ブラウザー
低いメモリ使用量と優れたパフォーマンスを同時に実現する方法はありますか?
ローカル変数(ここでは
el
)がガベージコレクターによってクリアできない場合、誰かが確認または確認できますか?または、最新のブラウザーはクロージャーで使用されていないことを検出できますか?
はい、最新のJavaScriptエンジンは、クロージャーからは見えるが未使用の親スコープから変数を検出できます。私はそれを証明する方法を見つけました。
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の開発者ツールの[プロファイル]タブで、ヒープスナップショットを取得します:
サイズを小さくして並べ替えると、最初の行は次のようになります: "system/JSArrayBufferData"サイズが10 MB。変数arr
です。
次のコード行でarr
パラメーターを削除します。
_ el.addEventListener("click", ev => this.onClick(ev))
_
次に、2番目のスナップショット:
最初の行は消えました。
この経験により、ガベージコレクターは、アクティブクロージャーでは表示されているが使用されていない親スコープから変数を削除できることを確認します。
Function.prototype.bind
_についてGoogle JavaScript Style Guide 、矢印関数のセクションを引用します:
f.bind(this)
またはgoog.bind(f, this)
を呼び出さないでください(_const self = this
_の記述は避けてください)。これらはすべて、矢印関数を使用してより明確に表現でき、エラーが発生しにくくなります。これは、予期しない追加の引数を渡すことがあるコールバックの場合に特に便利です。
Googleは、_Function.prototype.bind
_ではなくラムダを使用することを明確に推奨しています。
関連: