Maison  >  Article  >  interface Web  >  Une explication simple des fermetures en JS

Une explication simple des fermetures en JS

黄舟
黄舟original
2017-10-24 09:44:571203parcourir

1. "La fermeture consiste à accéder à des variables dans toutes les étendues."

[Exemple 1]


var name = 'wangxi'
function user () {
 // var name = 'wangxi'
 function getName () {
 console.log(name)
 }
 getName()
}
user() // wangxi

Pour obtenir le nom dans la fonction getName, recherchez d'abord le nom dans la portée de la fonction getName, mais il n'est pas trouvé. Ensuite, il recherche dans la portée de la fonction utilisateur. Il n'est pas non plus trouvé. vers le haut et trouvez que ce nom existe dans la portée globale, alors récupérez la valeur du nom et imprimez-la. C'est facile à comprendre ici, c'est-à-dire que les variables existent toutes dans la portée spécifiée. Si la variable souhaitée ne peut pas être trouvée dans la portée actuelle, la recherche se poursuivra dans la portée parent via la chaîne de portée jusqu'à la première avec la même. le nom est trouvé (ou s'il ne peut pas être trouvé, une ReferenceError sera levée). C'est le concept de chaîne de portées en js, c'est-à-dire que la portée enfant peut accéder aux variables de la portée parent en fonction de la chaîne de portées. Et si, au contraire, la portée parent voulait accéder aux variables de la portée enfant ? ——Cela doit être réalisé par la clôture.

[Exemple 2]


function user () {
 var name = 'wangxi'
 return function getName () {
 return name
 }
}
var userName = user()()
console.log(userName) // wangxi

En analysant le code, nous savons que ce nom est une variable locale qui existe dans le cadre de la fonction utilisateur Under. circonstances normales, dans La variable name n'est pas accessible dans la portée externe (voici le global), mais par fermeture (renvoyant une fonction contenant la variable, voici la fonction getName), la variable est accessible à travers les portées (accès externe interne ). Par conséquent, l'instruction ci-dessus doit être complètement comprise comme :

La fermeture consiste à accéder aux variables à travers les portées - la portée interne peut conserver une référence aux variables dans la portée externe afin que (plus) la portée externe puisse accéder aux variables dans la portée intérieure. (Si vous ne comprenez toujours pas, lisez l'analyse suivante)

2. "Clôture : Papa est exécuté dans l'environnement de grand-père, et le petit-fils est exécuté. renvoyé dans papa. À l'origine, après l'exécution du père, l'environnement du père doit être effacé, mais le petit-fils fait référence à l'environnement du père, donc le père ne peut pas le libérer. En termes simples, une fermeture est un objet qui fait référence à l'environnement du père. là. Un objet renvoyé de l'environnement parent vers l'environnement de niveau supérieur "

Comment comprendre cela ? Regardez d'abord le code ci-dessous :

[Exemple 3]


function user () {
 var name = 'wangxi'
 return name
}
var userName = user()
console.log(userName) // wangxi

Q : Est-ce une fermeture ?

R : Bien sûr que non. Tout d’abord, vous devez comprendre ce qu’est une fermeture. Bien qu'il semble que le nom de la variable locale dans la fonction utilisateur soit accessible dans la portée globale, le problème est qu'après l'exécution de l'utilisateur, le nom est également détruit, c'est-à-dire que le cycle de vie de la variable locale dans la fonction n'existe que pendant le cycle de déclaration de la fonction, la fonction est détruite et les variables à l'intérieur de la fonction sont automatiquement détruites.

Mais utiliser les fermetures est le contraire. Une fois la fonction exécutée, le cycle de vie se termine, mais les variables de la portée externe référencées par la fermeture existent toujours et existeront toujours jusqu'à ce que la portée qui exécute la fermeture soit terminée. Détruisez, les variables locales ici seront détruites (si la fermeture est référencée dans l'environnement global, la portée référencée par la fermeture ne sera détruite que lorsque l'environnement global sera détruit, comme le programme se termine, le navigateur est fermé, etc. ). Par conséquent, afin d’éviter les pertes de mémoire causées par les fermetures, il est recommandé de détruire manuellement les fermetures après utilisation. Toujours le même que l'exemple 2 ci-dessus, légèrement modifié :

[Exemple 4]


function user () {
 var name = 'wangxi'
 return function getName () {
 return name
 }
}
var userName = user()() // userName 变量中始终保持着对 name 的引用
console.log(userName) // wangxi
userName = null // 销毁闭包,释放内存

[Pourquoi user()() a deux parenthèses : L'exécution de user() renvoie la fonction getName. Pour obtenir la variable name, vous devez exécuter une fois la fonction getName renvoyée, c'est donc user()()】

Selon le point 2, analysez le code : dans la variable globale UserName (grand-père) est créée sous la portée et la référence au résultat final de retour de la fonction utilisateur est enregistrée (c'est-à-dire que la valeur du nom de la variable locale User()() (père) est). exécuté et le nom (petit-fils) est renvoyé dans des circonstances normales, après l'exécution de user()(), l'environnement de l'utilisateur (père) doit être effacé, mais parce que le nom du résultat renvoyé (petit-fils) fait référence à l'environnement du père (car le nom existe à l'origine dans la portée de l'utilisateur), de ce fait, l'environnement de l'utilisateur ne peut pas être libéré (ce qui entraînera une perte de mémoire).

Donc ["Une fermeture est un objet qui fait référence à l'environnement parent et renvoie un objet de l'environnement parent vers un environnement de niveau supérieur."] Comment comprendre ?

Disons les choses autrement : si une fonction fait référence à un objet dans l'environnement parent et renvoie l'objet à l'environnement de niveau supérieur dans cette fonction, alors cette fonction est une fermeture.

Regardez l'exemple ci-dessus :

La fonction getName fait référence à l'objet (nom de la variable) dans l'environnement utilisateur (parent) et renvoie la variable de nom à l'environnement global (niveau supérieur) dans l'environnement de la fonction), getName est donc une fermeture.

3. "Les fonctions en JavaScript s'exécutent dans la portée dans laquelle elles sont définies, et non dans la portée dans laquelle elles sont exécutées."

Cette phrase est très utile pour comprendre la référence aux variables dans les fermetures. Regardons l'exemple suivant :


var name = 'Schopenhauer'
function getName () {
 console.log(name)
}
function myName () {
 var name = 'wangxi'
 getName()
}
myName() // Schopenhauer

Si le résultat de l'exécution de myName() est différent de ce que vous imaginiez, vous devez revenir en arrière et regarder le mentionné ci-dessus Ça y est,

JavaScript 中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里

执行 myName,函数内部执行了 getName,而 getName 是在全局环境下定义的,因此尽管在 myName 中定义了变量 name,对getName 的执行并无影响,getName 中打印的依然是全局作用域下的 name。

我们稍微改一下代码:


var name = 'Schopenhauer'
function getName () {
  var name = 'Aristotle'
 var intro = function() { // 这是一个闭包
  console.log('I am ' + name)
 }
 return intro
}
function showMyName () {
 var name = 'wangxi'
 var myName = getName()
 myName()
}
showMyName() // I am Aristotle

结果和你想象的一样吗?结果留作聪明的你自己分析~

以上就是对 js 中闭包的理解,如果有误,欢迎指正。最后引用一段知乎问题下关于闭包概念的一个回答。

什么是闭包?

简单来说,闭包是指可以访问另一个函数作用域变量的函数,一般是定义在外层函数中的内层函数。

为什么需要闭包?

局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。

特点

  • 占用更多内存

  • 不容易被释放

何时使用?

变量既想反复使用,又想避免全局污染

如何使用?

  1. 定义外层函数,封装被保护的局部变量。

  2. 定义内层函数,执行对外部函数变量的操作。

  3. 外层函数返回内层函数的对象,并且外层函数被调用,结果保存在一个全局的变量中。

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