12
votes

Comment cette fonction de fermeture JavaScript réutilise-t-elle un objet sans variable globale?

J'ai décidé de faire un pas en avant d'essayer de comprendre JavaScript et de lire à nouveau JavaScript: Les bonnes parties . Et voici le premier doute:

Disons que je veux éviter d'utiliser les variables globales car ils sont diaboliques, et j'ai donc ce qui suit: xxx

d.crockford affirme que c'est lent car chaque fois la fonction est appelée, une nouvelle instanciation de noms est terminée. Alors, il passe à la solution la solution en faisant ceci: xxx

ceci fait les noms variable stocké en mémoire Et par conséquent, il n'est pas instancié à chaque fois que nous appelons digit_name .

Je veux savoir pourquoi? Lorsque nous appelons digit_name , pourquoi la première ligne est-elle "ignorée"? Qu'est-ce que je rate? Qu'est-ce qui se passe vraiment ici?

J'ai basé cet exemple non seulement dans le livre, mais sur cette vidéo (minute 26)

(Si quelqu'un pense à un meilleur titre, veuillez suggérer le cas échéant ...)


5 commentaires

Crockford suggère-t-il réellement cela? Quelle page? Cela rendrait beaucoup plus de sens s'il y avait une finale () à la fin de votre code, ce qui signifie que digit_name obtient la valeur de retour de la fonction extérieure, qui est le fonction interne.


Mon erreur. Oublié d'ajouter (); à la fin. Désolé les gars.


"Je veux éviter d'utiliser les variables globales parce qu'elles sont diaboliques", " - non, ils ne sont pas plus que des couteaux sont diaboliques - c'est comme ça que tu les utilises qui compte. (Et même alors, une utilisation inappropriée des globaux est ... inappropriée - pas mal.). Mais dans tous les cas, le code que vous commencez à éviter les globaux crée immédiatement un nom global ( digit_name ).


@nnnnnn: J'avais réellement citant D.Crockford, je l'ai entendu dire que dans deux conférences :)


Oui, M. Crockford semble tout voir en absolue. Je pense que les zones grises le rendent nerveuse. (Ne pas dire qu'il n'a pas beaucoup de bons conseils - le problème consiste à déterminer quels bits de conseils ne sont que ses préférences personnelles présentées comme des "faits".)


3 Réponses :


12
votes

Je suis sûr que vous vouliez faire fonctionner votre deuxième exemple une fonction d'exécution immédiate (c'est-à-dire une fonction d'exécution immédiate (I.e., auto-invoquant), comme ceci: xxx

La distinction implique une chaîne de portée avec des fermetures. Les fonctions de JavaScript ont une portée en ce sens qu'elles rechercheront les fonctions parent des variables qui ne sont pas déclarées dans la fonction elle-même.

Lorsque vous déclarez une fonction à l'intérieur d'une fonction en JavaScript, cela crée une fermeture. Une fermeture délimite un niveau de portée.

dans le deuxième exemple, digit_name est réglé égal à une fonction auto-invoquée. Cette fonction auto-invoquée déclare le tableau noms et renvoie une fonction anonyme.

digit_name devient donc: xxx

de votre exemple d'origine, vous pouvez voir que noms Est déclaré un niveau de niveau supérieur la chaîne d'étendue de la fonction anonyme retournée (qui est maintenant digit_name ). Lorsque cette fonction anonyme nécessite des noms , il déplace la chaîne d'étendue jusqu'à ce qu'elle trouve la variable déclarée - dans ce cas, noms est trouvé un niveau de niveau de la chaîne d'étendue. < / p>

en ce qui concerne l'efficacité:

Le deuxième exemple est plus efficace car noms est uniquement déclaré une fois - lorsque la fonction d'auto-invocation incendies (c.-à-d. Var digit_name = (fonction () {...}) ();); Lorsque digit_names est appelé, il recherchera la chaîne d'étendue jusqu'à ce qu'il trouve des noms .

dans votre premier exemple, noms se fait déclarer chaque fois digit_names est appelé, il est donc moins efficace.

Exemple graphique:

L'exemple que vous ' Le VE fourni de Douglas Crockford est un exemple assez difficile à commencer lors de l'apprentissage de la manière dont les chaînes de fermeture et de portée fonctionnent - beaucoup de choses emballées dans une minuscule quantité de code. Je recommanderais de jeter un coup d'œil à une explication visuelle des fermetures, comme celui-ci: http://www.bennadel.com/blog/1482-a-graphical-explanation-of-javascript-closures-in-a-jquery-context.htm < / p>


3 commentaires

"Lorsque vous déclarez une fonction à l'intérieur d'une fonction de JavaScript, qui crée une fermeture" - lorsque vous appelle une fonction déclarée à l'intérieur d'une autre fonction - chaque invocation a sa propre fonction fermeture...


Appeler une fonction déclarée à l'intérieur d'une autre fonction ne crée pas de nouvelle fermeture. Vous alliez réutiliser la fermeture (et chaque invocation aurait sa propre portée privée), mais ne crée pas une nouvelle.


Mes excuses, vous êtes correct. Ce que je voulais dire, c'est que chaque fois que vous appelez la fonction contenant crée une nouvelle fermeture. Je voulais dire que plus comme une clarification qu'une correction - vous pourriez dire que le Intérieur n'est déclaré que lorsque le contenant ne s'appelle que celui-ci, mais pour un nouveau venu au concept, ils pourraient examiner le code et que tout est déjà déclaré .. .



0
votes

Mettez simplement, le problème avec le premier code est que il crée un tableau sur chaque appel et renvoie une valeur de celle-ci. C'est une surcharge en raison du fait que vous créez un tableau chaque fois que vous appelez .

Dans le deuxième code, il crée une fermeture qui déclare un seul tableau et renvoie une fonction qui renvoie une valeur de ce tableau. Fondamentalement, digit_name porte maintenant sa propre matrice au lieu de faire un chaque appel. Votre fonction obtient à partir d'un tableau existant de la fermeture.


D'autre part, la fermeture, si elle n'est pas utilisée correctement et je vais manger de la mémoire. Les fermetures sont généralement utilisées pour protéger le code intérieur des étendues extérieures et généralement implémentées avec un accès limité de l'extérieur.

Les objets ne sont pas détruits par le GC à moins que toutes les références ne soient "annulées". Dans le cas de la fermeture, si vous ne pouvez pas y entrer de tuer ces références intérieures, les objets ne seront pas détruits par le GC et pour toujours manger de la mémoire.


1 commentaires

En fait, lorsque toutes les fonctions impliquées dans une fermeture dépassent de la portée, tous les objets associés obtiennent des ordures recueillies. Ceci est vrai pour tous les navigateurs. La seule exception sont des objets DOM contenant des références circulaires dans IE.



3
votes

Ce n'est pas une réponse, mais une clarification au cas où les exemples donnés semblent toujours déroutants.

Tout d'abord, clarifie. digit_name code> n'est pas la première fonction que vous voyez dans le code. Cette fonction est simplement créée pour renvoyer une autre fonction (oui, vous pouvez retourner des fonctions, comme vous pouvez renvoyer des chiffres ou des cordes ou des objets, des fonctions d'information sont des objets): P>

function digit_name_maker () {
    var names = ['zero', 'one', 'two', 'three'];

    return function (n) {
        return names[n];
    }
}

var digit_name = digit_name_maker(); // digit_name is now a function


0 commentaires