Portée
La portée est la portée de la fonction d'une variable et d'une fonction. Toutes les variables déclarées dans une fonction en JavaScript sont toujours visibles dans le corps de fonction. Il existe des lunettes mondiales et des lunettes locales en JavaScript, mais il n'y a pas de portée au niveau du bloc. La priorité des variables locales est supérieure à celle des variables globales. À travers plusieurs exemples, nous pouvons comprendre les "règles tacites" de portée en JavaScript (ce sont également des questions qui sont souvent posées dans les entretiens en front-end).
1. Déclaration variable à l'avance
Exemple 1:
var scope = "global"; fonction scopeTest () {console.log (scope); var scope = "local"} scopeTest (); //indéfiniLa sortie ici n'est pas définie et il n'y a pas d'erreur. En effet, les déclarations de la fonction que nous avons mentionnées ci-dessus sont toujours visibles dans le corps de la fonction. La fonction ci-dessus est équivalente à:
var scope = "global"; fonction scopeTest () {var scope; console.log (portée); scope = "local"} scopeTest (); //localeNotez que si VAR est oublié, la variable est déclarée comme une variable globale.
2. Pas de portée au niveau du bloc
Contrairement aux autres langues que nous utilisons couramment, il n'y a pas de portée au niveau du bloc en JavaScript:
function scopeTest () {var scope = {}; if (scope instanceOf objet) {var j = 1; pour (var i = 0; i <10; i ++) {//console.log(i); } console.log (i); // sortie 10} console.log (j); // sortie 1}En JavaScript, la portée de la fonction des variables est au niveau de la fonction, c'est-à-dire que toutes les variables de la fonction sont définies dans toute la fonction, ce qui apporte également des "règles tacites" que nous rencontrerons si nous ne faisons pas attention:
var scope = "Hello"; fonction scopeTest () {console.log (scope); // ① var scope = "no"; console.log (portée); // ②}La valeur de valeur à ① était en fait indéfinie, ce qui est fou. Nous avons défini la valeur de la variable globale. Cet endroit ne devrait-il pas être bonjour? En fait, le code ci-dessus équivaut à:
var scope = "Hello"; fonction scopeTest () {var scope; console.log (scope); // ① scope = "no"; console.log (portée); // ②}Déclarer les variables précoces et globales ont une priorité plus faible que les variables locales. Selon ces deux règles, il n'est pas difficile de comprendre pourquoi la sortie n'est pas définie.
Chaîne de portée
Dans JavaScript, chaque fonction a son propre contexte d'exécution. Lorsque le code est exécuté dans cet environnement, une chaîne de portée d'objets variables sera créée. La chaîne de portée est une liste d'objets ou une chaîne d'objets, qui garantit un accès ordonné aux objets variables.
L'extrémité avant de la chaîne de portée est l'objet variable de l'environnement d'exécution de code actuel, qui est souvent appelé "objet actif". La recherche de variables commence à partir de l'objet de la première chaîne. Si l'objet contient des attributs variables, la recherche sera arrêtée. Sinon, la recherche continuera de rechercher la chaîne de portée supérieure jusqu'à ce que l'objet global soit trouvé:
La recherche de chaînes de portée étape par étape affectera également les performances du programme. Plus la chaîne de portée des variables est longue, plus l'impact sur les performances est grande. C'est également une raison majeure pour laquelle nous essayons d'éviter d'utiliser des variables globales.
Fermeture
Concepts de base
La portée est une condition préalable à la compréhension des fermetures. Les fermetures se réfèrent à la possibilité d'accéder aux variables dans la portée externe dans la portée actuelle.
Fonction CreateClosure () {var name = "jack"; return {setstr: function () {name = "rose"; }, gettr: function () {return name + ": Bonjour"; }}} var builder = new CreateClosure (); builder.setstr (); console.log (builder.getstr ()); // Rose: BonjourL'exemple ci-dessus renvoie deux fermetures dans la fonction, qui maintiennent toutes deux des références à la portée externe, de sorte que les variables dans la fonction externe sont toujours accessibles partout où elles sont appelées. Les fonctions définies à l'intérieur d'une fonction ajouteront l'objet actif de la fonction externe à sa propre chaîne de portée. Par conséquent, dans l'exemple ci-dessus, la fonction interne peut accéder aux propriétés de la fonction externe via la fonction interne. C'est également un moyen pour JavaScript de simuler des variables privées.
Remarque: Étant donné que les fermetures auront des portées supplémentaires de fonctions (les fonctions anonymes internes portent des portées de fonctions externes), les fermetures occuperont plus d'espace mémoire que d'autres fonctions, et une utilisation excessive peut entraîner une utilisation accrue de la mémoire.
Variables dans les fermetures
Lorsque vous utilisez des fermetures, en raison de l'influence du mécanisme de la chaîne de portée, la fermeture ne peut obtenir que la dernière valeur de la fonction interne. Un effet secondaire de cela est que si la fonction interne est en boucle, la valeur de la variable est toujours la dernière valeur.
// Cette instance n'est pas raisonnable et a certains facteurs de retard. Ceci est principalement pour illustrer les problèmes dans la fonction de boucle de fermeture timeManage () {for (var i = 0; i <5; i ++) {setTimeout (function () {console.log (i);}, 1000)}; }Le programme ci-dessus ne saisit pas les nombres 1 à 5 comme nous l'avons prévu, mais les résultats 5 les 5 fois. Jetons un coup d'œil à un autre exemple:
Fonction CreateClosure () {var result = []; for (var i = 0; i <5; i ++) {result [i] = function () {return i; }} Retour Résultat;}Appeler CreateClosure () [0] () Renvoie 5, et CreateClosure () [4] () Renvoie la valeur est toujours 5. À partir des deux exemples ci-dessus, nous pouvons voir le problème qui existe des fermetures lors de l'utilisation de fonctions internes avec des boucles: parce que la chaîne de portée de chaque fonction se réfère à la même variable I. Lorsque la fonction externe revient, la valeur de I pour le moment est de 5, donc la valeur de chaque fonction interne I est également 5.
Alors, comment résoudre ce problème? Nous pouvons forcer le retour du résultat attendu grâce à un wrapper anonyme (expression de fonction auto-exécutée anonyme):
fonction timeManage () {for (var i = 0; i <5; i ++) {(function (num) {setTimeout (function () {console.log (num);}, 1000);}) (i); }}Ou renvoyez une affectation de fonction anonyme dans la fermeture de la fonction anonyme:
fonction timeManage () {for (var i = 0; i <10; i ++) {setTimeout ((function (e) {return function () {console.log (e);}}) (i), 1000)}} // timeManager (); Sortie 1,2,3,4,5Function CreateClosure () {var result = []; for (var i = 0; i <5; i ++) {result [i] = fonction (num) {return function () {console.log (num); } }(je); } Retour Résultat;} // CreateClosure () [1] () Sortie 1; CreateClosure () [2] () Sortie 2Qu'il s'agisse d'un wrapper anonyme ou de fonctions anonymes imbriquées, en principe, puisque la fonction est transmise par valeur, la valeur de la variable que je serai copiée dans le paramètre réel Num, et une fonction anonyme est créée à l'intérieur de la fonction anonyme pour retourner Num, afin que chaque fonction ait une copie de Num, qui ne se affectera pas mutuellement.
Ceci en fermeture
Portez une attention particulière lorsque vous utilisez cela dans les fermetures, car un peu de négligence peut causer des problèmes. Habituellement, nous comprenons que cet objet est lié par la fonction lors de l'exécution. Dans la fonction globale, cet objet est un objet de fenêtre. Lorsque la fonction est appelée méthode dans l'objet, cela est égal à cet objet (TODO fait un processus de tri à ce sujet). Étant donné que la portée des fonctions anonymes est globale, cette fermeture pointe généralement vers la fenêtre d'objet global:
var scope = "global"; var objet = {scope: "local", getScope: function () {return function () {return this.scope; }}}L'appel object.getScope () () renvoie la valeur globale au lieu du local que nous attendions. Nous avons dit plus tôt que les fonctions anonymes internes de la fermeture porteraient la portée de la fonction externe, alors pourquoi ne pas obtenir ceci de la fonction externe? Lorsque chaque fonction est appelée, ceci et les arguments seront automatiquement créés. Lors de la recherche de fonctions anonymes internes, ils recherchent les variables que nous voulons dans l'objet actif. Par conséquent, arrêtez de rechercher des fonctions externes, et il n'est jamais possible d'accéder directement aux variables dans les fonctions externes. En bref, lorsqu'une fonction est appelée méthode d'un objet dans une fermeture, il est important de prêter une attention particulière que cela dans la fonction anonyme dans la méthode pointe vers une variable globale.
Heureusement, nous pouvons résoudre ce problème très simplement, il suffit de stocker cela dans la portée de la fonction externe dans une variable accessible par une fermeture:
var scope = "global"; var objet = {scope: "local", getScope: function () {var that = this; return function () {return that.scope; }}} object.getScope () () () Renvoie la valeur locale.Mémoire et performances
Étant donné que la fermeture contient la même référence de la chaîne de portée que le contexte d'exécution de la fonction, il aura un certain effet négatif. Lorsque l'objet actif et le contexte d'exécution de la fonction sont détruits, l'objet actif ne peut pas être détruit car il y a toujours une référence à l'objet actif, ce qui signifie que la fermeture occupe plus d'espace mémoire que les fonctions ordinaires, et peut également provoquer une fuite de mémoire dans le navigateur IE, comme suit:
Fonction BindEvent () {var Target = document.getElementById ("elem"); Target.OnClick = function () {console.log (Target.Name); }}Dans l'exemple ci-dessus, la fonction anonyme génère une référence à la cible d'objet externe. Tant que la fonction anonyme existe, la référence ne disparaîtra pas et l'objet cible de la fonction externe ne sera pas détruit, ce qui crée une référence circulaire. La solution consiste à réduire les références circulaires aux variables externes en créant une copie de Target.name et réinitialisez manuellement l'objet:
Fonction BindEvent () {var Target = document.getElementById ("elem"); var name = cible.name; Target.OnClick = function () {console.log (name); } cible = null; }S'il y a accès à des variables externes dans la fermeture, le chemin de recherche des identifiants sera sans aucun doute ajouté, et dans certaines circonstances, cela entraînera également des pertes de performances. Nous avons mentionné plus tôt: essayez de stocker des variables externes en variables locales pour réduire la longueur de recherche des chaînes de portée.
Résumé: Les fermetures ne sont pas propres à JavaScript, mais elles ont leurs propres manifestations uniques en JavaScript. En utilisant les fermetures, nous pouvons définir certaines variables privées en JavaScript et même imiter les lunettes de niveau bloc. Cependant, lors de l'utilisation des fermetures, nous devons également comprendre les problèmes existants afin d'éviter des problèmes inutiles.