一、闭包的定义
闭包是一个函数以及周边环境状态(词法作用域)的引用组合。(画重点:闭包是函数,并且引用外部变量)换而言之,闭包可以让开发者从函数内部访问外部的函数的作用域。在JS中,闭包随着函数的创建而被创建。好处可以保护数据私有,坏处可能会影响性能,比如内存泄漏!!(注意:闭包引起的内存泄漏是不恰当使用而造成的,所以,谨慎使用闭包)
init()
创建了一个局部变量 name
和一个名为 displayName()
的函数。displayName()定义在init()里的内部函数,仅在init()函数体内可用。(块级作用域)displayName()
没有自己的局部变量。然而,因为它可以访问到外部函数的变量,(形成闭包)所以 displayName()
可以使用父函数 init()
中声明的变量 name
。
扩展:词法,词法作用域根据源代码声明变量位置来确定变量在何处可用。嵌套函数可访问声明于他的外部作用域的变量。
二、在循环中创建闭包:一个常见的错误
在ECMAScript 2015引入let关键字之前,在循环中有一个常见的闭包创建问题。
运行这段代码后,你会发现它没有达到想要的效果。无论焦点在哪个 input
上,显示的都是关于年龄的信息。
原因是赋值给 onfocus
的是闭包。这些闭包是由他们的函数定义和在 setupHelp
作用域中捕获的环境所组成的。这三个闭包在循环中被创建,但他们共享了同一个词法作用域,在这个作用域中存在一个变量 item。这是因为变量 item
使用 var
进行声明,由于变量提升,所以具有函数作用域。当 onfocus
的回调执行时,item.help
的值被决定。由于循环在事件触发之前早已执行完毕,变量对象 item
(被三个闭包所共享)已经指向了 helpText
的最后一项。
可以使用立即执行函数或者将var改成let即可解决,本质上就是每次循环都为item创建新的块级作用域。