Maison >interface Web >js tutoriel >Compréhension approfondie des fermetures JavaScript

Compréhension approfondie des fermetures JavaScript

小云云
小云云original
2017-12-06 15:16:061670parcourir

Une fermeture contient des variables libres (non liées à un objet spécifique) ; ces variables ne sont pas définies dans ce bloc de code ou dans un contexte global, mais sont définies dans l'environnement dans lequel le bloc de code est défini (variables locales). Le mot « fermeture » vient de la combinaison d'un bloc de code à exécuter (car les variables libres sont contenues à l'intérieur du bloc, ces variables libres et les objets auxquels elles font référence ne sont pas libérées) et de la liaison prévue pour les variables libres. Environnement informatique (portée). Dans cet article, nous vous donnerons une compréhension approfondie des fermetures JavaScript.

1. La portée des variables

Pour comprendre les fermetures, vous devez d'abord comprendre la portée des variables spéciales de Javascript.

La portée des variables n'est rien de plus que deux types : les variables globales et les variables locales.

La particularité du langage Javascript est que les variables globales peuvent être lues directement à l'intérieur de la fonction.


Code Js

 var n=999;

Fonction f1(){
alert(n);
 }

 f1(); // 999

Par contre, la fonction ne peut pas être lue en dehors du fonction.

Code Js

 fonction f1(){
 var n=999;
 }

alerte(n) ; // erreur

Il y a quelque chose à noter ici Lorsque vous déclarez des variables à l'intérieur d'une fonction, vous devez utiliser la commande var. Si vous ne l'utilisez pas, vous déclarez en fait une variable globale !

Code Js

 fonction f1(){
 n=999;
 }

 f1();

 alerte(n); // 999

---------------- -------------------------------------------------- ----------------------------------------------------

2 . Comment lire les variables locales de l'extérieur ?

Pour diverses raisons, nous avons parfois besoin d'obtenir des variables locales au sein d'une fonction. Cependant, comme mentionné précédemment, cela n’est pas possible dans des circonstances normales et ne peut être réalisé que grâce à des solutions de contournement.

C'est-à-dire définir une fonction à l'intérieur de la fonction.

Code Js

 fonction f1(){

 n=999;

function f2(){
   alert(n); Dans le code, la fonction f2 est incluse dans la fonction f1 À ce stade, toutes les variables locales à l'intérieur de f1 sont visibles par f2. Mais l’inverse ne fonctionne pas. Les variables locales à l’intérieur de f2 sont invisibles pour f1. Il s'agit de la structure unique de « portée de chaîne » du langage Javascript
L'objet enfant recherchera les variables de tous les objets parents niveau par niveau. Par conséquent, toutes les variables de l’objet parent sont visibles par l’objet enfant, mais l’inverse n’est pas vrai.
Puisque f2 peut lire les variables locales dans f1, alors tant que f2 est utilisé comme valeur de retour, ne pouvons-nous pas lire ses variables internes en dehors de f1 ?

Code Js

 

fonction f1(){

n=999;


 function f2(){

  alert(n);  }

  return f2 >

var result=f1( );

résultat();

// 999

----- ----------------------------------- --------------- ----------------------------------- --------------- -

3. La notion de fermeture

La fonction f2 dans le code de la section précédente est une fermeture.

La définition de « clôture » dans diverses littératures professionnelles est très abstraite et difficile à comprendre. Je crois comprendre qu'une fermeture est une fonction qui peut lire les variables internes d'autres fonctions. Étant donné que dans le langage Javascript, seules les sous-fonctions à l'intérieur de la fonction peuvent lire les variables locales, les fermetures peuvent être simplement comprises comme des « fonctions définies à l'intérieur d'une fonction ». Ainsi, par essence, la fermeture est un pont reliant l'intérieur d'une fonction à l'extérieur de la fonction.

------------------------------------------------------ ------ -------------------------------------------- ------ ----------b

4. L'utilisation des fermetures

Les fermetures peuvent être utilisées dans de nombreux endroits. Il a deux utilisations principales, l'une consiste à lire les variables à l'intérieur de la fonction comme mentionné précédemment, et l'autre est de conserver les valeurs de ces variables en mémoire . Comment comprendre cette phrase ? Veuillez regarder le code ci-dessous.

Code Js


 

fonction f1(){

 var n=999;

 nAdd=function(){n+=1}

  function f2(){

  alert(n);  }

Retour f2;

}

var result=f1( 🎜> result(); / 999

nAdd(); // 1000

Dans ce code, le résultat est en fait la fonction de fermeture f2. Il a été exécuté deux fois, la première fois, la valeur était de 999, la deuxième fois, la valeur était de 1 000. Cela prouve que la variable locale n dans la fonction f1 est toujours stockée en mémoire et n'est pas automatiquement effacée après l'appel de f1.

Pourquoi cela se produit-il ? La raison en est que f1 est la fonction parent de f2 et que f2 est affecté à une variable globale, ce qui fait que f2 est toujours en mémoire, et l'existence de f2 dépend de f1, donc f1 est toujours en mémoire et ne sera pas supprimé. une fois l'appel terminé, recyclé par le mécanisme de récupération de place (garbage collection). Une autre chose à noter dans ce code est la ligne "nAdd=function(){n+=1}". Tout d'abord, le mot-clé var n'est pas utilisé avant nAdd, donc nAdd est plutôt une variable globale. que les variables locales. Deuxièmement, la valeur de nAdd est une fonction anonyme, et cette fonction anonyme elle-même est également une fermeture, donc nAdd est équivalent à un setter, qui peut opérer sur des variables locales à l'intérieur de la fonction en dehors de la fonction.

------------------------------------------------------ ------ -------------------------------------------- ------ ----------

5. Points à noter lors de l'utilisation des fermetures

1) Parce que les fermetures créeront des fonctions. Les variables sont toutes stockées en mémoire, ce qui consomme beaucoup de mémoire, donc les fermetures ne peuvent pas être abusées, sinon cela entraînerait des problèmes de performances sur la page Web et pourrait provoquer des fuites de mémoire dans IE. La solution consiste à supprimer toutes les variables locales inutilisées avant de quitter la fonction.

2) La fermeture modifiera la valeur de la variable à l'intérieur de la fonction parent en dehors de la fonction parent. Par conséquent, si vous utilisez la fonction parent comme objet, la fermeture comme méthode publique et les variables internes comme valeur privée, vous devez faire attention à ne pas modifier la valeur de la variable à l'intérieur la fonction parent. ------------------------------------------------------ ------ -------------------------------------------- ------ ----------

6. Questions

Si vous pouvez comprendre les résultats de le code suivant, ça devrait aller Comprendre le mécanisme de fonctionnement des fermetures.

Code Jsvar name = "La fenêtre"; > return function(){ return this.name }; ;

alert(object.getNameFunc()());

// La fenêtre




---------- ----------------------- --------------------------- ----------------------- --------------------------




Exemple de fermeture JavaScript

fonction externalFun()

{

var a=0; function innerFun() { a++;
alert(a);
}innerFun()

Le code ci-dessus est

erreur. La portée de innerFun() est à l'intérieur de externalFun(), et elle est appelée à l'extérieur de externalFun(). C'est faux. Remplacez

par ce qui suit, qui est une fermeture :

Code Js

function externalFun()
{
var a=0;
function innerFun()
{
a++;
alert(a);
}
return innerFun; //Notez ici
>
var obj=outerFun();
obj();//Le résultat est 1
obj();
//Le résultat est 2
var obj2=outerFun();
obj2(
//Le résultat est 1
obj2() ;
//Le résultat est 2

Qu'est-ce qu'une fermeture :

Quand une fonction interne est référencée en dehors de la portée dans laquelle elle est définie , il est créé Pour la fermeture de la fonction interne, si la fonction interne fait référence à des variables situées dans la fonction externe, ces variables ne seront pas libérées en mémoire après l'appel de la fonction externe, car la fermeture les nécessite

<.>--------------------------------------------------------------- ------ -------------------------------------------- ------

Regardons un autre exemple Code Js

fonction externalFun()

{ var a =0;
alert(a);
>
var a=4;
outerFun();
alert(a);

résultat C'est

0,4

Parce que le mot-clé var est utilisé à l'intérieur de la fonction pour maintenir la portée d'un inside outFun(). encore une fois : Js Code

function externalFun()

{

//No var
a =0;
alert(a); } var a=4;
outerFun();
alert(a);

Le résultat est
0,0
C'est vraiment étrange, pourquoi ?

La chaîne de portée est un terme qui décrit un chemin le long duquel la valeur d'une variable peut être déterminée. Lorsque a=0 est exécuté, parce que le mot-clé var n'est pas utilisé, l'opération d'affectation. suivra la chaîne de portée jusqu'à var a= 4; Et changera sa valeur.

------------------------- --------- ----------------------------------------- --------- ----------------------------------------- --------- ------------

Si vous ne comprenez pas très bien les fermetures JavaScript, veuillez lire l'article réimprimé ci-dessous : (Réimprimé : http://www.felixwoo.com/archives/247)



1. Qu'est-ce que la clôture ?

L'explication officielle est la suivante : une fermeture est une expression (généralement une fonction) qui a de nombreuses variables et un environnement lié à ces variables, donc ces variables en font également partie Je crois que peu de gens peuvent comprendre cette phrase directement, car sa description est trop académique. En fait, cette phrase est : Toutes les fonctions en JavaScript sont un package

. la fermeture générée par la fonction imbriquée est plus puissante, ce que l'on appelle la plupart du temps « fermeture » :


Ce code a deux caractéristiques :

1. imbriqué dans la fonction a
function a() { 
 var i = 0; 
 function b() { alert(++i); } 
 return b;}var c = a();c();

2. La fonction a renvoie la fonction b

La relation de référence est la suivante :

De cette façon. , après avoir exécuté var c=a(), la variable c pointe en fait vers la fonction b. Après avoir exécuté c(), une fenêtre apparaîtra pour afficher la valeur i (la première fois est 1). Ce code crée en fait une fermeture. Pourquoi ? Parce que la variable c en dehors de la fonction a fait référence à la fonction b à l'intérieur de la fonction a, c'est-à-dire :

 

Quand la fonction interne b de la fonction a est référencée par une variable en dehors de la fonction a, une fermeture est créée


Rendons les choses plus approfondies. Certains, ce qu'on appelle la "fermeture", consiste à définir une autre fonction comme fonction de méthode de l'objet cible dans. le corps du constructeur, et la fonction méthode de cet objet fait à son tour référence à la variable temporaire dans le corps de la fonction externe. Cela fait que tant que l'objet cible est vivant, sa méthode peut toujours être conservée dans l'objet, et la variable temporaire. La valeur utilisée par le corps du constructeur d'origine peut être indirectement conservée. Bien que l'appel initial du constructeur soit terminé et que les noms des variables temporaires aient disparu, elles sont toujours dans la méthode de l'objet cible. La valeur de la variable peut toujours être référencée. et la valeur n'est accessible que via cette méthode. Même si le même constructeur est appelé à nouveau, seuls les nouveaux objets et méthodes seront générés, et les nouvelles variables temporaires ne correspondront qu'aux nouvelles valeurs. >
2. Quelle est la fonction de la fermeture ?

En bref, la fonction de la fermeture est Après l'exécution et le retour de a, la fermeture empêche le mécanisme de récupération de place de Javascript. GC de récupérer les ressources occupées par a, car l'exécution de la fonction interne de a b dépend des variables de a. Il s'agit d'une explication très simple du rôle de la fermeture. La description n'est ni professionnelle ni rigoureuse, mais le sens général est le suivant. . Comprendre la fermeture nécessite un processus étape par étape .

在上面的例子中,由于闭包的存在使得函数a返回后,a中的i始终存在,这样每次执行c(),i都是自加1后alert出i的值。

  那 么我们来想象另一种情况,如果a返回的不是函数b,情况就完全不同了。因为a执行完后,b没有被返回给a的外界,只是被a所引用,而此时a也只会被b引 用,因此函数a和b互相引用但又不被外界打扰(被外界引用),函数a和b就会被GC回收。(关于Javascript的垃圾回收机制将在后面详细介绍)

三、闭包内的微观世界

  如果要更加深入的了解闭包以及函数a和嵌套函数b的关系,我们需要引入另外几个概念:函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)。以函数a从定义到执行的过程为例阐述这几个概念。

  1. 定义函数a的时候,js解释器会将函数a的作用域链(scope chain)设置为定义a时a所在的“环境”,如果a是一个全局函数,则scope chain中只有window对象。

  2. 执行函数a的时候,a会进入相应的执行环境(excution context)

  3. 在创建执行环境的过程中,首先会为a添加一个scope属性,即a的作用域,其值就为第1步中的scope chain。即a.scope=a的作用域链。

  4. 然后执行环境会创建一个活动对象(call object)。活动对象也是一个拥有属性的对象,但它不具有原型而且不能通过JavaScript代码直接访问。创建完活动对象后,把活动对象添加到a的作用域链的最顶端。此时a的作用域链包含了两个对象:a的活动对象和window对象。

  5. 下一步是在活动对象上添加一个arguments属性,它保存着调用函数a时所传递的参数。

  6. 最后把所有函数a的形参和内部的函数b的引用也添加到a的活动对象上。在这一步中,完成了函数b的的定义,因此如同第3步,函数b的作用域链被设置为b所被定义的环境,即a的作用域。

到此,整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给c,又函数b的作用域链包含了对函数a的活动对象的引用,也就是说b可以访问到a中定义的所有变量和函数。函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。

当函数b执行的时候亦会像以上步骤一样。因此,执行时b的作用域链包含了3个对象:b的活动对象、a的活动对象和window对象,如下图所示:

如图所示,当在函数b中访问一个变量的时候,搜索顺序是:

  1. 先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。

  2. 如果函数b存在prototype原型对象,则在查找完自身的活动对象后先查找自身的原型对象,再继续查找。这就是Javascript中的变量查找机制。

  3. 如果整个作用域链上都无法找到,则返回undefined。

小结,本段中提到了两个重要的词语:函数的定义执行。文中提到函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定(参看步骤1和3)。用一段代码来说明这个问题:

function f(x) { 
  var g = function () { return x; }  return g;}var h = f(1);alert(h());

这段代码中变量h指向了f中的那个匿名函数(由g返回)。

  • 假设函数h的作用域是在执行alert(h())确定的,那么此时h的作用域链是:h的活动对象->alert的活动对象->window对象。

  • 假设函数h的作用域是在定义时确定的,就是说h指向的那个匿名函数在定义的时候就已经确定了作用域。那么在执行的时候,h的作用域链为:h的活动对象->f的活动对象->window对象。

如果第一种假设成立,那输出值就是undefined;如果第二种假设成立,输出值则为1。

运行结果证明了第2个假设是正确的,说明函数的作用域确实是在定义这个函数的时候就已经确定了。

四、闭包的应用场景
保护函数内的变量安全。以最开始的例子为例,函数a中i只有函数b才能访问,而无法通过其他途径访问到,因此保护了i的安全性。

  1. Maintient une variable en mémoire. Toujours comme dans l'exemple précédent, en raison de la fermeture, i dans la fonction a existe toujours en mémoire, donc à chaque fois que c() est exécuté, i sera incrémenté de 1.

  2. Implémentez les propriétés et méthodes privées JS en protégeant la sécurité des variables (non accessibles en externe)
    Les propriétés et méthodes privées ne sont pas accessibles en dehors du constructeur

    fonction Constructeur(...){
    var que = ce;
    var nom de membre = valeur;
    fonctionnom du membre(...){...}
    }

Les trois points ci-dessus sont les scénarios d'application les plus élémentaires des fermetures. De nombreux cas classiques en découlent.

5. Le mécanisme de récupération de place de Javascript

En Javascript, si un l'objet n'est plus référencé, l'objet sera recyclé par GC. Si deux objets font référence l'un à l'autre et ne sont plus référencés par un tiers, alors les deux objets qui font référence l'un à l'autre seront également recyclés. Parce que la fonction a est référencée par b, et b est référencé par c en dehors de a, c'est pourquoi la fonction a ne sera pas recyclée après exécution.

Comprendre les fermetures JavaScript est le seul moyen de devenir un programmeur JS avancé. Ce n'est qu'en comprenant son interprétation et son mécanisme de fonctionnement que vous pourrez écrire un code plus sûr et plus élégant. Le contenu ci-dessus est une explication détaillée des fermetures JavaScript. J'espère qu'il pourra aider tout le monde.

Recommandations associées :

Explication détaillée des formes courantes de fermetures JS

Explication détaillée des fermetures JS classiques

Explication détaillée des connaissances js Closure_basic

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn