闭包总结

闭包 : 内部函数使用了外部函数的变量 , 那么内部函数和这个变量之间的环境就叫闭包

闭包的特点 , 在闭包里保存了外部函数的变量,即使函数死亡了,只要闭包还在,那么变量就一直还活跃在内存中

应用1 : for循环问题

在js中var定义的变量没有块级作用域一说, 只有局部和全局变量

for循环里是一个{}块级作用域

用var定义时就成了全局变量

var btn=document.getElementsByTagName('button')
for (var i = 0; i < 5; i++) {
    btn[i].onclick=function () {
        alert(i)
    }
}

上面这段代码每次执行for循环的时候,对应的给每个按钮绑定了一个函数, 但是函数的内容是alert(i), 绑定的时候并没有执行函数 , 当回调函数执行的时候, for循环就已经执行完了 , 每个按钮都已经绑定上了函数, 而i只有一个 每次循环就++;

当for循环执行完的时候 , i就已经等=4了

所以当onclick回调函数执行时 , alert的i就是全局的那个i(4)

使用闭包

for (var i = 0; i < 5; i++) {
    (function (j) {
        btn[j].onclick=function () {
            alert(j)
        }
    })(i)
}

在for循环里定义一个立即执行函数, 每次循环就会执行一次这个函数, 在这个函数里定义Dom回调函数 , 那么这个匿名函数就回调函数的外部函数 ,

在匿名函数里参数里声明一个变量(参数里的变量即在函数体力var一个变量) , js都是值传递, 那么传递参数的时候就可以将每次for循环的值传进这个匿名函数里 , 在回调函数里使用外部函数的变量 , 就产生了一个闭包 , 每次执行for由于dom对象的不同, 那么就每次都生成一个闭包 , 这个闭包和函数一起绑定给了dom的回调函数 , 闭包里保存的每次执行时的外部函数(匿名函数)的变量, 即每一次的i值

那么当dom回调函数执行的时候, alert会在当前作用域里寻找这个i 恰好闭包里保存了这个i 所以执行的时候就可以正常输出了

总结: 利用闭包将变量的生存周期延长 , 并封闭在闭包里 , 达到了模仿块级作用域的效果
在这里插入图片描述
在这里插入图片描述

上面匿名函数产生的闭包可以在dom元素绑定的onclick事件的回调函数里保存

使用let,const

es6中的变量let和const都是支持块级作用域的

使用let定义i时,每一次for循环执行时都是一个新的块级作用域, 这里有5个i , 分别在不同的区块中 , 因此每次dom元素的onclick被触发时, 就直接到它绑定的回调函数所在的区块中去寻找这个i

补充:
js函数中的scopes是函数在定义时开辟的作用域给赋值的 , 里面说明了函数所能访问到的作用域和对应的作用域里的数据

到函数执行的时候, 就会在这里面找它所需要的数据

应用2:封装

js不同于java对象拥有私有变量的 , 但是js可以通过闭包来实现变量私有化

上面应该也提到过 , 在一个js函数中, 把关键数据都定义在外部, 在内部函数里只提供有限的操作 , 以达到封装的目的 , 因此就有了模块化

function myModule() {
  //私有数据
  var msg = 'My atguigu'
  //操作数据的函数
  function doSomething() {
    console.log('doSomething() '+msg.toUpperCase())
  }
  function doOtherthing () {
    console.log('doOtherthing() '+msg.toLowerCase())
  }

  //向外暴露对象(给外部使用的方法)
  return {
    doSomething: doSomething,
    doOtherthing: doOtherthing
  }
}

上面一个js模块 , 完成了特定功能 ,只向外暴露了想要给你操作方法 , 执行这个函数时将暴露给的函数接收即可 , 之前应该说过外部函数里的变量全部死亡了 ,但是内部函数指向的那个对象重新被接收了 , 因此可以一直保持活跃

除了上述的暴露方法之外, 还有一种暴露方法更加简洁

(function (window) {
  //私有数据
  var msg = 'My atguigu'
  //操作数据的函数
  function doSomething() {
    console.log('doSomething() '+msg.toUpperCase())
  }
  function doOtherthing () {
    console.log('doOtherthing() '+msg.toLowerCase())
  }

  //向外暴露对象(给外部使用的方法)
  window.myModule2 = {
    doSomething: doSomething,
    doOtherthing: doOtherthing
  }
})(window)

使用立即执行函数, 引入这个js文件时就立即执行了这个函数

该函数将需要暴露的对象给了window属性, 因此引入这个函数之后不需要执行也不需要接收, 直接就像使用全局变量一样使用即可

上面一般在立即执行函数里添加一个widow参数 , 立即执行函数接收并作为全局对象来使用
因为后续压缩代码的时候, 会将里面的参数用a,b等代替, 可能会引起不必要的麻烦

补充:
当想要使用闭包封装的作用时 , 必须要暴露 , 不然封装就没有意义 外部函数执行了闭包产生后就消失了 完全没必要也没任何意义

闭包缺点

函数执行完之后如果不手动释放闭包所在的函数的话 , 那么就会一直保存在内存里 容易造成内存泄漏

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值