一个简单的闭包
var arg = 111
function fn() {
var arg = 100
return function add() {
return (arg += 100)
}
}
console.log(fn()) // add方法
console.log(fn()()) // 200
console.log(arg) // 111
// console.log(arg) //arg is not defined
// console.log(add()) //add is not defined
var pg1 = fn()
var pg2 = fn()
console.log(pg1()) // 200
console.log(pg2()) // 200
这个简单的闭包表明:
1、函数中创建的变量arg只能在函数中使用(变量的函数作用域)
2、子函数只能通过父函数调用,在外部无法调用
同时,这个简单的闭包也突出了其优点:
1、避免变量的污染,既不影响其他全局变量,其他变量也不会影响函数中的变量
2、可以通过一个闭包的定义形式,创建pg1,pg2等多个含相同变量和方法的闭包,同时,这些闭包的中的变量和方法相互独立、互不影响
总之,一个闭包中可以包含私有变量、私有方法,还可以通过一个闭包的定义形式创建多个相互独立的闭包
包含私有变量和私有方法的闭包
function fn(xing, ming) {
var xing = xing,
ming = ming
return {
xing: function () {
return xing
},
ming: function () {
return ming
},
name: function () {
return xing + ming
}
}
}
var me = fn('li', 'si')
var you = fn('wang', 'wu')
console.log(me.name())
console.log(you.name())
for循环中的闭包
在没有ES6的let块作用域变量声明之前,for循环中用var声明循环变量:
for (var i = 0; i < Array.length; i++) {
// 业务代码
}
1、如果for循环中的业务代码是同步代码,则按照循环顺序依次正常进行
2、如果for循环中的业务代码是异步代码,会出现一个问题,例如在业务代码中给多个元素绑定点击事件:
<div>1</div>
<div>2</div>
<div>3</div>
var divs = document.querySelectorAll('div')
for (var i = 0; i < divs.length; i++) {
divs[i].onclick = function () {
console.log('点击了第', i, '个div') // 点击了第 3 个div
}
}
想要得到的结果:点击1div,打印点击了第1个div,点击2,打印点击了第2个div...
但实际中,无论点击哪一个div,都只会打印点击了第3个div
原因:事件处理程序是异步的,每次循环并没有将 i 赋给对应的事件行为,当循环结束后,i的值变为3,for循环中声明的i是全局变量,这时点击事件触发都只会输出i为3时的处理行为
解决:使用匿名闭包,为每次循环绑定的点击事件行为创建一个局部作用域,设置相互独立、互不影响的变量和方法体
将事件绑定的业务代码改为:
var divs = document.querySelectorAll('div')
for (var i = 0; i < divs.length; i++) {
;(function () {
var index = i
divs[i].onclick = function () {
console.log('点击了第', index, '个div') // 点击了第 0,1,2 个div
}
})()
}
当ES6出现let之后,for循环的场景下就不用再使用闭包来修改这个问题,而是通过let声明循环变量来解决
本文含有隐藏内容,请 开通VIP 后查看