Maison >interface Web >js tutoriel >Continuez à acquérir les compétences javascript close_javascript

Continuez à acquérir les compétences javascript close_javascript

WBOY
WBOYoriginal
2016-05-16 15:27:55979parcourir

1. Qu'est-ce que la fermeture ?

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 font également partie de l'expression
. Je pense que peu de gens peuvent comprendre directement cette phrase car sa description est trop académique. En fait, cette phrase en termes simples signifie : toutes les fonctions en JavaScript sont des fermetures. Mais de manière générale, la fermeture générée par les fonctions imbriquées est plus puissante, et c'est ce que l'on appelle la plupart du temps « fermeture ». Regardez le code suivant :

function a() { 
 var i = 0; 
 function b() { alert(++i); } 
 return b;
}
var c = a();
c();

Ce code a deux caractéristiques :

1. La fonction b est imbriquée dans la fonction a

2. La fonction a renvoie la fonction b.

La relation de référence est telle qu'indiquée sur la figure :

De cette façon, après avoir exécuté var c=a(), la variable c pointe en fait sur la fonction b. Après avoir exécuté à nouveau c(), une fenêtre apparaîtra pour afficher la valeur de 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 dans la fonction a, c'est-à-dire :

 Lorsque la fonction interne b de la fonction a est référencée par une variable extérieure à la fonction a, une fermeture est créée.

Soyons plus minutieux. Ce qu'on appelle la « fermeture » consiste à définir une autre fonction dans le corps du constructeur comme fonction de méthode de l'objet cible, et la fonction de méthode de cet objet fait à son tour référence à la variable temporaire dans le corps de fonction externe. Cela permet de conserver indirectement les valeurs des variables temporaires utilisées par le corps du constructeur d'origine tant que l'objet cible peut toujours conserver ses méthodes pendant sa durée de vie. Bien que l'appel initial du constructeur soit terminé et que le nom de la variable temporaire ait disparu, la valeur de la variable peut toujours être référencée dans la méthode de l'objet cible et la valeur n'est accessible que via cette méthode. Même si le même constructeur est appelé à nouveau, seuls de nouveaux objets et méthodes seront générés, et les nouvelles variables temporaires ne correspondent qu'à de nouvelles valeurs, indépendantes du dernier appel.

2. Quelle est la fonction de la fermeture ?

En bref, la fonction de la fermeture est qu'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 doit dépendre de a. variables dans . Il s’agit d’une description très simple du rôle des fermetures. Elle n’est ni professionnelle ni rigoureuse, mais le sens général est le suivant : Comprendre les fermetures nécessite un processus étape par étape.

Dans l'exemple ci-dessus, en raison de l'existence d'une fermeture, i dans a existera toujours après le retour de la fonction a. De cette façon, chaque fois que c() est exécuté, i sera la valeur de i qui est alertée après l'ajout. 1.

Alors imaginons une autre situation. Si a renvoie autre chose que la fonction b, la situation est complètement différente. Parce qu'après l'exécution de a, b n'est pas renvoyé au monde extérieur de a, mais n'est référencé que par a. À ce stade, a ne sera référencé que par b. Par conséquent, les fonctions a et b se réfèrent l'une à l'autre mais ne sont pas perturbées. par le monde extérieur (référencé par le monde extérieur), les fonctions a et b seront recyclées par GC. (Le mécanisme de récupération de place de Javascript sera présenté en détail plus tard)

3. Le monde microscopique au sein de la fermeture

Si nous voulons avoir une compréhension plus approfondie des fermetures et de la relation entre la fonction a et la fonction imbriquée b, nous devons introduire plusieurs autres concepts : contexte d'exécution de la fonction (contexte d'exécution), objet actif (objet d'appel), portée (portée ) ), chaîne de portée. Prenons comme exemple le processus de la fonction a, de la définition à l'exécution, pour illustrer ces concepts.

  • Lors de la définition de la fonction a, l'interpréteur js définira la chaîne de portée (chaîne de portée) de la fonction a sur "l'environnement" où se trouve a lors de la définition de a. une fonction globale, alors il n'y a que l'objet window dans la chaîne de portée.
  • Lors de l'exécution de la fonction a, a entrera dans le contexte d'exécution correspondant.
  • Dans le processus de création de l'environnement d'exécution, un attribut de portée sera d'abord ajouté à a, c'est-à-dire la portée d'un , et la valeur de est la chaîne de portée à l'étape 1. Autrement dit, la chaîne de portées de a.scope=a.
  • Ensuite, l'environnement d'exécution créera un objet d'appel. L'objet actif est également un objet qui a des propriétés, mais il n'a pas de prototype et n'est pas accessible directement via le code JavaScript. Après avoir créé l'objet actif, ajoutez l'objet actif en haut de la chaîne de portées de a. À l'heure actuelle, la chaîne de portée de a contient deux objets : l'objet actif de a et l'objet window.
  • L'étape suivante consiste à ajouter un attribut arguments sur l'objet actif, qui contient les paramètres passés lors de l'appel de la fonction a.
  • Enfin, ajoutez tous les paramètres formels de la fonction a et les références à la fonction interne b à l'objet actif de a. Dans cette étape, la définition de la fonction b est terminée, donc tout comme à l'étape 3, la chaîne de portée de la fonction b est définie sur l'environnement dans lequel b est défini, c'est-à-dire la portée de a.

À ce stade, les étapes allant de la définition à l'exécution de l'ensemble de la fonction a sont terminées. À ce stade, a renvoie la référence de la fonction b à c, et la chaîne de portée de la fonction b contient une référence à l'objet actif de la fonction a, ce qui signifie que b peut accéder à toutes les variables et fonctions définies dans a. La fonction b est référencée par c et la fonction b dépend de la fonction a, donc la fonction a ne sera pas recyclée par GC après son retour.

Lorsque la fonction b est exécutée, ce sera la même chose que les étapes ci-dessus. Par conséquent, la chaîne de portée de b lors de l’exécution contient 3 objets : l’objet actif de b, l’objet actif de a et l’objet fenêtre, comme le montre la figure suivante :

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

  • 先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。
  • 如果函数b存在prototype原型对象,则在查找完自身的活动对象后先查找自身的原型对象,再继续查找。这就是Javascript中的变量查找机制。
  • 如果整个作用域链上都无法找到,则返回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的安全性。

在内存中维持一个变量。依然如前例,由于闭包,函数a中i的一直存在于内存中,因此每次执行c(),都会给i自加1。
通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)
私有属性和方法在Constructor外是无法被访问的

function Constructor(...) { 
 var that = this; 
 var membername = value; 
 function membername(...) {...}
}

以上3点是闭包最基本的应用场景,很多经典案例都源于此。

五、Javascript的垃圾回收机制

在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。

六、结语

理解了aScript的闭包的解释和运行机制才能写出更为安全和优雅的代码,希望对大家的学习有所帮助。

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