理解JavaScript闭包

JAVASCRIPT ginotang 730℃ 0评论

什么是闭包

在MDN中,闭包的定义如下:

闭包是一个函数和函数所声明的词法环境的结合。

什么意思,看下面的代码

上面的代码实际上就是一个闭包,该代码定义了两个函数和一个变量,而且,内部函数访问了外部函数中定义的变量。因此,闭包产生的条件有两个,且缺一不可:

  1. 存在一个返回值为函数的函数(父函数的返回值为子函数)
  2. 子函数访问了父函数中的变量

闭包的作用

使外部环境可以访问内部环境中的变量

JavaScript变量作用域链的特点是,子函数总是可以访问父函数中的变量,但反过来不成立。外部作用域永远不可能访问内部作用域中定义的变量。

但现实是,我们总会遇到需要获取函数内部变量的情况,那么,闭包就是其中一种解决办法。

当然,可以把protectedValue定义为一个全局变量,但是,这种行为是非常危险的,一旦项目变得庞大,过多的全局变量就会使项目代码变得混乱而难以维护。

让变量常驻内存

闭包的另一个作用是可以使变量常驻内存。一般来说,当函数运行完毕,它的变量就会被销毁,借助闭包,我们就可以延长变量的作用域。

例如:如果我们想一个变量在函数每次运行的时候都自增1:

当然,上面的代码无论运行多少次,counter的值始终都是1,因为每次函数执行完毕,它占用的内存就会被销毁,等到下次运行的时候再载入内存。

一个可行的办法:

这段代码中,只要func变量未被销毁,那么内存中的add就不会被销毁。而func作为全局对象window的属性,只要浏览器还没有关闭,那么func所占用的内存就一直存在,所以,整个作用域链被保存下来。

再举一个例子,计算代码的运行效率:

相比起第一个作用,闭包的第二个作用显得更加重要。

闭包的模块化支持

以前的JavaScript编程中,主要是过程式编程的,通过闭包,可以模仿面向对象编程,达到数据的隐藏和封装的效果。

例如:

当然,这不是一个使用闭包的最佳例子,这个例子中的闭包单单用来返回数据,有点小题大做的意味。

使用闭包保存状态

在继续之前,有必要介绍一下立即执行函数(或者叫自执行函数),这种函数在定义完成的时候就马上执行。我们都知道,函数每次执行的时候都会创建一个执行上下文(执行环境),每个环境中的内存变量是独立的。

创建自执行函数非常简单:

可以为自执行函数添加一个名称,但是因为它是定义的时候马上就执行,所以这么做有点多余。和普通的函数一样,可以为自执行函数传递参数:

一旦函数执行,上下文中就保存了传递进去的变量值。

现在,我们继续说如何使用闭包保存状态这个问题。有一个需求,文档中有三个li标签,当鼠标点击这个标签的时候输出相应的标签索引:

上面的代码是有问题的,当鼠标点击li标签的时候,for循环已经完成,因此i的值已经增加到3,所以无论点哪一个li标签,输出的值都是3。

现在使用闭包改进代码:

由于使用了自执行函数,它的上下文保存了每一次循环的时候传递的值,因此代码可以正确运行。

还有另一种写法,但上面的代码更具可读性

 

转载请注明:Pure nonsense » 理解JavaScript闭包

喜欢 (0)
0 0 投票数
文章评分
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x
()
x