Maison >interface Web >js tutoriel >Une explication simple des fermetures en JS

Une explication simple des fermetures en JS

小云云
小云云original
2018-01-04 09:30:371459parcourir

Tout le monde a entendu parler des fermetures dans JS. Cet article présente les fermetures dans JS à travers un exemple de code. Il a une valeur de référence. Les amis intéressés devraient y jeter un œil.

1. "La fermeture consiste à accéder aux 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, d'abord dans getName. function Recherchez le nom dans la portée, mais il n'est pas trouvé Ensuite, nous recherchons dans la portée de la fonction utilisateur, mais il n'est pas non plus trouvé. Nous continuons à remonter et trouvons que le nom existe dans la portée globale, donc nous. obtenez 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 name est une variable locale qui existe dans la portée de la fonction utilisateur. Dans des circonstances normales, elle se trouve dans la portée externe. (voici le global) La variable name n'est pas accessible, mais par fermeture (renvoyant une fonction contenant la variable, voici la fonction getName), les variables sont accessibles à 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 : le papa est exécuté dans l'environnement du grand-père, et le petit-fils est rendu dans le père. A l'origine, le père a été exécuté, et l'environnement du père doit être effacé, mais le petit-fils a fait référence à l'environnement du père, donc le père n'a pas pu le libérer. En termes simples, une fermeture est un objet qui fait référence à l'environnement parent et le renvoie à un environnement de niveau supérieur. "

Comment comprenez-vous 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 crochets : l'exécution de user() renvoie la fonction getName, dans afin d'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 : la variable userName est créée dans la portée globale (grand-père ), enregistre la référence au résultat final de retour de la fonction utilisateur (c'est-à-dire la valeur du nom de la variable locale), exécute user()() (père) et renvoie le nom (petit-fils). après avoir exécuté user()() Après cela, l'environnement de l'utilisateur (père) doit être effacé, mais comme 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), l'environnement de l'utilisateur ne peut pas être libéré (cela 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 explique l'utilisation des variables dans les fermetures. est utile. 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 lire la phrase ci-dessus,

Fonctions en JavaScript, exécutés dans la portée dans laquelle ils sont définis, et non dans la portée dans laquelle ils sont exécutés

执行 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. 外层函数返回内层函数的对象,并且外层函数被调用,结果保存在一个全局的变量中。

相关推荐:

PHP中的闭包和匿名函数分析

深入理解javascript闭包

详解JavaScript作用域和闭包

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