闭包详解和应用

发布于:2022-12-02 ⋅ 阅读:(711) ⋅ 点赞:(0)

一个简单的闭包

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 后查看