Je voudrais déterminer quelle est la meilleure pratique entre des solutions équivalentes. Le cas d'utilisation est une instance d'une classe qui écoute un événement. Dr. Axel Rauschmayer préfère le lambda pour la lisibilité. Je suis d'accord avec lui. Mais en termes de performances et de consommation mémoire, quelle est la meilleure?
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)
}
}
Quelqu'un peut-il confirmer ou infirmer si les variables locales (ici el
) ne peuvent pas être effacées par le garbage collector? Ou, les navigateurs modernes sont-ils capables de détecter qu'ils ne sont pas utilisés dans la fermeture?
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)
}
}
Il n'y a pas de problème de mémoire, mais tous les benchmarks suggèrent que bind
est bien plus lent qu'une fermeture (un exemple ici ).
EDIT: je ne suis pas d'accord avec les commentaires qui ignorent le problème de performances de bind
. Je suggère de lire cette réponse avec le code de l'implémentation dans Chrome. Cela ne peut pas être efficace. Et j'insiste: tous les repères que j'ai vus, montrent des résultats similaires sur tous navigateurs.
Existe-t-il un moyen d'avoir une faible utilisation de la mémoire et de bonnes performances en même temps?
Quelqu'un peut-il confirmer ou infirmer si les variables locales (ici
el
) ne peuvent pas être effacées par le garbage collector? Ou, les navigateurs modernes sont-ils capables de détecter qu'ils ne sont pas utilisés dans la fermeture?
Oui , les moteurs JavaScript modernes sont capables de détecter des variables des portées parents qui sont visibles à la fermeture mais non utilisées. J'ai trouvé un moyen de le prouver.
J'ai utilisé ce code dans 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()
Remarquez la variable arr
de type Uint8Array
. Il s'agit d'un tableau typé avec une taille de 10 mégaoctets. Dans cette première version, la variable arr
est utilisée dans la fermeture.
Ensuite, dans les outils de développement de Chromium, onglet "Profils", je prends un Heap Snapshot:
Après avoir trié par taille décroissante, la première ligne est: "system/JSArrayBufferData" avec une taille de 10 Mo. C'est notre variable arr
.
Maintenant, je supprime simplement le paramètre arr
dans cette ligne de code:
el.addEventListener("click", ev => this.onClick(ev))
Ensuite, un deuxième instantané:
La première rangée a disparu.
Cette expérience confirme que le garbage collector est capable de nettoyer les variables des étendues parent qui sont visibles mais inutilisées dans les fermetures actives.
Function.prototype.bind
Je cite le Google JavaScript Style Guide , section sur les fonctions fléchées:
N'appelez jamais
f.bind(this)
ougoog.bind(f, this)
(et évitez d'écrireconst self = this
). Tous ces éléments peuvent être exprimés plus clairement et moins sujets aux erreurs avec une fonction de flèche. Ceci est particulièrement utile pour les rappels, qui passent parfois des arguments supplémentaires inattendus.
Google recommande clairement d'utiliser des lambdas plutôt que Function.prototype.bind
.
En relation: