Portée et fermetures

En JavaScript, scope correspond au contexte dans lequel le code est exécuté. Il existe trois types de portée: portée globale, portée locale (parfois appelée "portée de la fonction") et portée eval..

Code défini à l'aide de var L’intérieur d’une fonction est délimité localement et n’est "visible" que par les autres expressions de cette fonction, ce qui inclut le code à l’intérieur de toute fonction imbriquée / enfant. Les variables définies dans l'étendue globale sont accessibles de n'importe où car il s'agit du niveau le plus élevé et du dernier arrêt de la chaîne d'étendue..

Examinez le code qui suit et assurez-vous de bien comprendre que chaque déclaration de foo est unique en raison de sa portée.

Échantillon: sample110.html

 

Assurez-vous de comprendre que chaque foo La variable contient une valeur différente, car chacune d’elles est définie dans une portée spécifiquement définie..

Vous pouvez créer un nombre illimité d’étendues de fonctions et d’évaluations, alors qu’un seul environnement global est utilisé par un environnement JavaScript..

La portée globale est la dernière étape de la chaîne de la portée.

Les fonctions contenant des fonctions créent des étendues d'exécution empilées. Ces piles, qui sont enchaînées, sont souvent appelées la chaîne de la portée..


JavaScript n'a pas de portée de blocage

Depuis les déclarations logiques (si) et des instructions en boucle (pour) ne créez pas de portée, les variables peuvent s’écraser. Examinez le code suivant et assurez-vous de bien comprendre que la valeur de foo est redéfini alors que le programme exécute le code.

Échantillon: sample111.html

 

Alors foo est en train de changer au fur et à mesure de l'exécution du code car JavaScript n'a pas de portée d'étendue de bloc, ni globale, ni d'évaluation.


Utilisation var À l'intérieur des fonctions permettant de déclarer des variables et d'éviter les pièges liés à l'étendue

JavaScript déclarera toutes les variables dépourvues de var déclaration (même celles contenues dans une fonction ou des fonctions encapsulées) d'être dans la portée globale au lieu de la portée locale prévue. Regardez le code qui suit et remarquez que sans l'utilisation de var pour déclarer bar, la variable est définie dans la portée globale et non dans la portée locale, où elle devrait être.

Échantillon: sample112.html

 

Le concept à retenir ici est que vous devriez toujours utiliser var lors de la définition de variables à l'intérieur d'une fonction. Cela vous évitera de traiter avec des problèmes de portée potentiellement déroutants. L'exception à cette convention, bien sûr, est lorsque vous souhaitez créer ou modifier des propriétés dans la portée globale à partir d'une fonction.


The Scope Chain (aka Lexical Scoping)

Une chaîne de recherche est suivie lorsque JavaScript recherche la valeur associée à une variable. Cette chaîne est basée sur la hiérarchie de la portée. Dans le code qui suit, je consigne la valeur de sayHiText du func2 portée de la fonction.

Échantillon: sample113.html

 

Comment est la valeur de sayHiText trouvé quand il n'est pas contenu dans le champ d'application du func2 une fonction? JavaScript commence par apparaître dans le func2 fonction pour une variable nommée sayHiText. Ne pas trouver func2 là-bas, il semble func2s fonction parent, func1. le sayHiText variable ne se trouve pas dans le func1 portée, soit, donc JavaScript continue ensuite jusqu'à la portée globale où sayHiText est trouvé, à quel point la valeur de sayHiText est livré. Si sayHiText n'avait pas été défini dans la portée globale, indéfini aurait été retourné par JavaScript.

C'est un concept très important à comprendre. Examinons un autre exemple de code, dans lequel nous prenons trois valeurs de trois portées différentes..

Échantillon: sample114.html

 

La valeur pour z est local à la bar fonction et le contexte dans lequel le console.log est invoqué. La valeur pour y est dans le foo fonction, qui est le parent de bar(), et la valeur pour X est dans la portée globale. Tous ces éléments sont accessibles au bar fonction via la chaîne de l'oscilloscope. Assurez-vous de comprendre que le référencement des variables dans le bar la fonction vérifiera tout au long de la chaîne de la portée pour les variables référencées.

La chaîne de la portée, si vous y réfléchissez, n'est pas si différente de la chaîne des prototypes. Les deux sont simplement un moyen de rechercher une valeur en vérifiant un ensemble d'emplacements systématique et hiérarchique..


La chaîne de recherche Scope renvoie la première valeur trouvée

Dans l’exemple de code qui suit, une variable appelée X existe dans le même champ dans lequel il est examiné avec console.log. Cette valeur "locale" de X est utilisé, et on pourrait dire qu'il ombres, ou masques, le même nom X les variables trouvées plus haut dans la chaîne de la portée.

Échantillon: sample115.html

 

N'oubliez pas que la recherche de portée se termine lorsque la variable est trouvée dans le lien disponible le plus proche de la chaîne, même si le même nom de variable est utilisé plus haut dans la chaîne..


L'étendue est déterminée lors de la définition de la fonction, pas l'invocation

Comme les fonctions déterminent la portée et que les fonctions peuvent être transmises comme n'importe quelle valeur JavaScript, on pourrait penser que déchiffrer la chaîne de la portée est compliqué. C'est en fait très simple. La chaîne d'étendue est décidée en fonction de l'emplacement d'une fonction lors de la définition, pas lors de l'appel. Cela s'appelle également la portée lexicale. Réfléchissez longuement à cela, car la plupart des gens trébuchent souvent dessus en code JavaScript..

La chaîne d'étendue est créée avant que vous appeliez une fonction. De ce fait, nous pouvons créer des fermetures. Par exemple, une fonction peut renvoyer une fonction imbriquée à l'étendue globale, mais notre fonction peut toujours accéder, via la chaîne d'étendue, à l'étendue de sa fonction parente. Dans l'exemple suivant, nous définissons un parentFunction qui retourne une fonction anonyme, et nous appelons la fonction retournée à partir de la portée globale. Parce que notre fonction anonyme a été définie comme étant contenue à l'intérieur de parentFunction, il a toujours accès à fonctions parentales portée quand il est invoqué. Ceci s'appelle une fermeture.

Échantillon: sample116.html

 

L'idée que vous devriez retenir ici est que la chaîne d'étendue est déterminée lors de la définition, littéralement dans la manière dont le code est écrit. Passer des fonctions à l'intérieur de votre code ne changera pas la chaîne de l'oscilloscope.


Les fermetures sont causées par la chaîne de la portée

Prenez ce que vous avez appris sur la chaîne et la recherche de portée de cet article, et une fermeture ne devrait pas être trop compliquée à comprendre. Dans l'exemple suivant, nous créons une fonction appelée countUpFromZero. Cette fonction renvoie en fait une référence à la fonction enfant qu’elle contient. Lorsque cette fonction enfant (fonction imbriquée) est appelée, elle a toujours accès à la portée de la fonction parent en raison de la chaîne de portée..

Échantillon: sample117.html

 

A chaque fois le countUpFromZero fonction est appelée, la fonction anonyme contenue dans (et renvoyée de) la countUpFromZero fonction a toujours accès à la portée de la fonction parent. Cette technique, facilitée via la chaîne du scope, est un exemple de fermeture.


Conclusion

Si vous croyez que j'ai des fermetures trop simplistes, vous avez probablement raison dans cette idée. Mais je l’ai fait à dessein, car j'estime que les parties importantes proviennent d’une solide compréhension des fonctions et de la portée, pas nécessairement de la complexité du contexte d’exécution. Si vous avez besoin d'une plongée en profondeur dans les fermetures, consultez JavaScript Closures.