手机版

JavaScript闭包详解

时间:2021-10-14 来源:互联网 编辑:宝哥软件园 浏览:

在最后一篇文章中,我们总结了预解释。写这篇博文之前,我们计划写几个经典案例。考虑到那些案例比较全面,我们一步步有了这篇博文,让学习和深化JavaScript变得更加容易。

命令

同事一去面试,面试官就问了一个问题:要不要写封信给我看?于是同事们火速写下了下面的代码:

复制的代码如下: function fn(){ alert(' hello JavaScript闭包!');//妈,e文字不好,找翻译的时候只能写结尾词} fn();

然后面试官摇摇头说:“这怎么能叫收尾呢?”最后,两人发生争执,同事果断离开。面试官做了什么?(这个故事纯属虚构,如果相似,纯属巧合。)

封闭可能是很多人眼中“又高又坏”的技术,只有这样才算是封闭:

例1:

复制代码如下: function fn(){ return function(){ alert('示例1 ');} } fn()();

1 PS:这个看起来不是很高级。看来这个人的水平不是很好啊!

例2:

复制代码如下:(function () {alert('示例2 ');})();

例2 PS:这个看起来比上一个高,第一个括号前加了分号。为什么要加分号?好了,先把这个问题留在这里,以后再说。

例3:

复制代码如下: ~函数fn() {alert('示例3 ')}();

例3 PS:这是最先进的,是很可怕的东西。我学习不多,不要骗我!

我读书不多,只能写这三个“闭包”。相信博主可以写出更多更好的“闭包”。至此,请先暂停我的废话,再研究函数运行的机制。好像已经有人知道了,肯定是范围。我真的不想把这个范围加到标题里。感觉总是很有趣。这些东西都在一起,为什么还要重复?旧习惯,代码优先:

复制代码如下:var n=10函数fn(){ alert(n);var n=9;警报(n);} fn();

简单来说,我们画图(业主只会用Windows自带的画图软件,如果有更好的,请博主推荐)分析:

分析1

从图中我们可以看到两个作用域,一个是窗口作用域(顶层作用域),另一个是调用fn时形成的私有作用域。什么是范围?范围实际上是代码执行的环境。比如一个学生的学习环境是学校,也就是说他的范围是学校。如果学生很调皮,晚上经常去网吧打游戏,说明已经形成了一个私密的环境,这个范围就是网吧。好吧。这个栗子太TM像主人本人了,我不禁感叹:“你年轻不努力,长大了会被踢的。”。回到正题,其实函数fn的定义是指对一段代码的描述(图中红框)。调用该fn时(图中绿框),会形成一个范围。当然,这个范围内的代码将在执行之前进行预解释。我不会告诉你这个范围在执行后会被销毁。如果再次调用该fn,将形成一个新的作用域。然后在执行前进行预解释,然后执行代码,最后执行。

理解闭包。

我们知道,当一个函数被调用时,它会形成一个私有范围(执行环境),这个私有范围就是闭包。回望闭包依然是传说中的“个子高不好”?让我们回顾一下第一个采访故事和我写的三个例子,其实都是闭包。准确地说,这三个例子都是闭包的常见形式。

应用场景

现在有一个需求:HTML页面有一个ul标签,ul下有五个li标签。需要随意点击任意li,被点击的li的索引会弹出(索引从0开始)。HTML的结构如下:

副本代码如下:ul ID=' ul '列表1/li列表2/li列表3/li列表4/li列表5/li/ul。我很快写了以下代码:

复制代码如下:Varlis=document。getElementByid ('ul ')。getElementSBYTAGNAME(' Li ');for (var i=0,len=lis.length我透镜;i ) { lis[i]。onclick=function(){ alert(I);};}

最后,测试一下这个需求是否完全实现了:

发现无论点击多少次,最终都会弹出这个结果,想要的结果是:点击列表1弹出0,点击列表2弹出1,点击列表3弹出2.此时此刻,我只想用这张图来形容我现在的心情:

(演示过程中样机未按设计要求运行时)。

这怎么会好呢?为什么总是弹出5?理论上是正确的!我们画个图来分析一下:

实际上,我们给每个li的onclick实际上是一个保存的函数描述字符串。这个字符串的内容是上面红色框中的内容。如果你还不相信,我有一张带有真相的图片:

输入:清单[4]。Chrome控制台下的onclick,它的值是函数的描述。当我们点击第五个列表时,它实际上相当于清单[4]。onclick(),并调用此函数描述。我们知道,函数被调用执行时,会形成一个私有作用域,在这个私有作用域下,会先进行预解释,然后再执行代码。这个时候会找到我,但是在当前私有范围下没有我,然后在窗口范围下会找到我,所以每次点击都会弹出5。

显然,上面的代码不能满足这个要求。编写我们的代码是不正确的。让我们想想是什么导致了这个问题。其实原因是每次点击,我在窗口下面都是阅读。这时,这个I的值已经是5,所以我有下面的代码:

方法1:

复制代码如下:Varlis=document。getElementByid ('ul ')。getElementSBYTAGNAME(' Li ');函数fn(i) {返回函数(){ alert(I)};}}for (var i=0,len=lis.length我透镜;i ) { lis[i]。onclick=fn(I);}

方法2:

复制代码如下:Varlis=document。getElementByid ('ul ')。getElementSBYTAGNAME(' Li ');for (var i=0,len=lis.length我透镜;I){;(函数(i) { lis[i]。onclick=function(){ alert(I);};})(I);}

方法3:

复制代码如下:Varlis=document。getElementByid ('ul ')。getElementSBYTAGNAME(' Li ');for (var i=0,len=lis.length我透镜;i ) { lis[i]。onclick=function fn(I){ return function(){ alert(I);} }(I);}

我一口气写了三种方式,他们的思路都是一样的,就是把这个变量I存储为私有变量。我在这里只讲第二种方式,当然,如果我理解其中一种,其余的也会理解。按照惯例,我们一步一步画图来分析:

我详细描述了整个代码执行的过程,需要注意的是,每个li的onclick属性占用了(function(i){ … })(i)的范围,这个函数在执行后不会被破坏,因为被外面的li占用了(这个li在window的范围之下),所以这个范围不会被破坏。当您单击任意li时,function(){ alert(I);}将被执行,并将形成一个范围。如果这个作用域中没有I,就会去(function(){ … })(i)在作用域中找I,最后在参数中找I。在for循环中传递了这个参数I的值;这个例子巧妙地使用闭包来存储值,完美地解决了这个问题。

PS:刚才说(函数(i){ … })(i)为什么前面加分号?原因是为了防止前面的语句忘记额外的符号,这将导致JavaScript在解析时出错,仅此而已。当然,上面的应用场景之一就是tab实现原理,可以有其他的实现方法,比如用户自定义属性的方法,通过DOM节点关系寻找索引,而拥有者只是采用这样的方法来加深对闭包的理解。

摘要

闭包不是传说中的大问题,但它的核心是理解函数定义和调用。当一个函数被调用时,会形成一个新的私有作用域。当一个范围被外界占用时,这个范围不会被破坏。我读书不多,请指正,谢谢大家对我文章的支持。

版权声明:JavaScript闭包详解是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。