Qu'est-ce que la fermeture ? Vous voyez à quoi servent les fermetures ? L'article suivant parlera des fermetures en JavaScript. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il sera utile à tout le monde.
Dans le processus d'apprentissage front-end, nous rencontrerons inévitablement de nombreux problèmes, c'est pourquoi aujourd'hui nous parlerons de deux questions du point de vue d'un débutant :
Qu'est-ce qu'une fermeture ?
Quelles sont les fonctions des fermetures ?
En fait, les fermetures sont partout quand on apprend le javascript, il suffit de pouvoir les reconnaître et les accepter. Les fermetures ne sont pas un outil qui nécessite l'apprentissage d'une nouvelle syntaxe ou d'un nouveau modèle. Les fermetures sont une conséquence naturelle de l'écriture de code basé sur la portée lexicale. Nous avons rarement besoin de créer intentionnellement des fermetures lors de l'écriture de code.
Je crois que beaucoup d'amis marmonnent déjà dans leur cœur en ce moment, quelle est cette portée lexicale ? Ne paniquez pas, écoutez-moi lentement. Bref, la portée lexicale est la portée définie au stade lexical. En d'autres termes, la portée lexicale est déterminée par l'endroit où vous placez les variables et les portées au niveau du bloc lorsque vous écrivez votre code, de sorte que la portée reste inchangée lorsque l'analyseur lexical traite le code (la plupart du temps). ——"Volume JavaScript que vous ne connaissez pas"
Prenons d'abord un exemple
function test(){ var arr = [] for(var i=0;i<10;i++){ arr[i]=function(){ console.log(i); } } return arr } var myArr = test() // myArr[0]() // myArr[1]() // ... for(var j = 0; j < 10; j++){ myArr[j]() } //为了避免繁琐此处使用了第二个循环来调用test函数里第一个循环里函数的打印出十个结果
Analysons d'abord ce code : Lorsque ce code est exécuté, selon une analyse de bon sens, dix nombres de 0 à 9 doivent être imprimés en séquence ; mais la boucle for ne prend pas de temps à s'exécuter (négligeable en microsecondes). Lorsque le test de fonction renvoie arr, arr[. ] À l'intérieur se trouvent 10 function(){console.log(i);} À ce stade, les fonctions du tableau ne sont pas exécutées lorsque var myArr = test() appelle la fonction de test, depuis le temps d'exécution de la boucle for. est ignoré, ceci A ce moment, i vaut déjà 10, donc 10 10 sont imprimés.
Je crois que quelqu'un demandera à ce moment-là, qu'est-ce que cela a à voir avec la fermeture dont nous parlons ? Donc si nous modifions légèrement ce code et le transformons en accumulateur, comment pouvons-nous l'implémenter ?
Je crois qu'il y aura des gros bonnets en ce moment pour dire que n'est-ce pas si simple ?
Modifiez la définition de var en let definition afin que la première boucle for devienne une portée au niveau du bloc, puis elle puisse devenir un accumulateur. Bien sûr, il n'y a pas de problème,
Mais ce dont nous parlons aujourd'hui, c'est de savoir comment implémenter un accumulateur dans ES5. Jetons ensuite un coup d'œil au code suivant :
function test(){ var arr = [] for(var i=0;i<10;i++){ (function(j){ arr[j]=function(){ console.log(j); } })(i) } return arr } var myArr = test() for(var j = 0; j < 10; j++){ myArr[j]() }
Les amis prudents découvriront certainement qu'il s'agit simplement de changer le corps de la fonction dans la boucle en une fonction auto-exécutable, mais le résultat de sortie à ce moment est Dix nombres sont sortis de 0 à 9 dans une séquence, qui contient des fermetures. Lorsque nous commençons à exécuter ce code, la deuxième boucle for sera appelée dix fois. Lorsque chaque fonction auto-exécutable est exécutée, une fonction auto-exécutable sera créée. L'objet AO de la fonction en cours d'exécution. Il y a un attribut nommé j dans l'objet AO de la fonction auto-exécutable, une fois l'exécution de la fonction auto-exécutable terminée, son objet AO doit être détruit, mais lorsque myarr. [j] ( ) est exécuté, le nom d'attribut j est recherché dans l'objet AO de arr[j] en haut de la chaîne de portée, mais il n'est pas trouvé. Ensuite, nous recherchons dans la chaîne de portée et le trouvons dans. l'objet AO de la fonction auto-exécutable, donc lorsque la fonction auto-exécutable Lorsque la fonction d'exécution se termine, son objet AO ne sera pas recyclé par le mécanisme de récupération de place, sinon une erreur sera signalée lorsque myarr[j] () est exécuté, et une clôture sera formée à ce moment-là.
Prenons un autre exemple
function a(){ function b(){ var bbb = 234 console.log(aaa); } var aaa = 123 return b // b出生在a里面,但是被保存出去了 } var glob = 100 var demo = a() demo()
Nous utilisons d'abord la précompilation pour analyser ce code. Tout d'abord, nous définissons un objet GO global. déclaration, recherchez la déclaration de variable globale et utilisez la déclaration de variable comme nom d'attribut de GO, la valeur étant indéfinie. Pour la déclaration globale, recherchez la déclaration de fonction, utilisez le nom de fonction comme nom d'attribut de l'objet GO, et attribuez la valeur au corps de la fonction. À ce stade, il devrait s'agir de GO{ glob: undefined--->100; Créez ensuite un AO{ aaa: undefined--->123;b: fb(){} } pour la fonction a, et enfin précompilez la fonction b dans la fonction a pour créer un AO{ b: undefined-- ->234} ;L'ordre de la chaîne de portée à ce moment est 1. Objet AO de la fonction b; 2. Objet AO de la fonction a 3. Objet GO global. Lorsque nous imprimons aaa dans la fonction b, nous commençons par le haut de la chaîne de portée. S'il n'y a pas d'aaa dans l'objet AO de la fonction b, nous rechercherons dans la chaîne de portée l'AO de la fonction a de deuxième niveau. Le but est de trouver la valeur de aaa égale à 123 et d'afficher le résultat.
如果我们没有从预编译的角度去分析就会认为此时的aaa应该会报错的,当var demo = a()执行时,当a函数执行结束,那么a对应的AO对象应该被销毁了,照常理分析当我们执行demo时作用域链此时应该会创建b的AO对象和GO对象,此时只有b的AO对象,没有a的AO对象,应该不能打印出aaa的值,但是此时aaa的值为123,则说明a的AO对象没有被销毁,那么为什么呢?原因就在于这里创建了闭包,当var demo = a()执行结束之后,垃圾回收机制会来问,a函数老兄,我看你都执行完毕了,你的运行内存是不是可以给我释放了,但是此时a函数只能无奈摇摇头说道,老哥,我也不确定我有没有执行完毕,我执行完创建了一个b,但是b又不归我管,所以我也不确定b有没有被调用,所以我也不确定我有没有执行完毕,垃圾回收机制想了想,既然你都不知道那我就不回收了,要是回收了还没执行完的就该报错了,所以此时a的AO对象就没有被回收。
补充全面一点就是:当一个函数内部的函数被保存到函数外部时,就会产生闭包。
相信通过这两个例子,你已经对闭包有了一个大概的了解,那接下来我们说一下闭包有哪些作用。
闭包的作用
- 实现公有变量 例如:累加器(3.js)
- 做缓存
- 可以实现封装,属性私有化
- 模块化开发,防止污染全局变量
我们对闭包的作用也来一个例子(3.js)
var count = 0 function add() { return count++ } console.log(add()); console.log(add()); console.log(add());Copier après la connexion这是一段比较普通的累加的代码,但是如果我们在实习甚至是工作的时,公司要求你把累加器封装成一个模块化的代码,那么
此时,为了模块化我们尽可能避免定义全局变量,但是不定义全局变量我们如何实现呢,此时我们就可以用到闭包了;function add() { var count = 0 function a() { ++count console.log(count); } return a } var res = add() res() res() //add函数结束之后,add的AO对象没有被销毁,因为add函数执行完了之后,返回的a不知道是否被调用就形成了闭包,这样 就能使得不使用全局变量也能封装成一个模块化的累加器。Copier après la connexion结语
那么关于闭包以及闭包的作用相关的一些个人见解就是这些,目前对于闭包也只是一些浅显的了解,后期学习之后完善过后会出后续关于闭包的相关文章,感谢您的观看,欢迎批评斧正,一起共同进步。
【相关推荐:javascript视频教程、web前端】
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!