web-dev-qa-db-fra.com

Fonctions lambda vs bind, mémoire! (et performances)

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?

Avec une fonction lambda

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?

Avec 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?

21
Paleo

Les fermetures (ou les fonctions fléchées, alias lambdas) ne provoquent pas de fuites de mémoire

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.

Étape 1: la fermeture utilise une variable de 10 Mo

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:

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

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.

Étape 2: la variable de 10 Mo est visible mais inutilisée dans la fermeture

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é:

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

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.

À propos de Function.prototype.bind

Je cite le Google JavaScript Style Guide , section sur les fonctions fléchées:

N'appelez jamais f.bind(this) ou goog.bind(f, this) (et évitez d'écrire const 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:


19
Paleo