浅谈闭包
浅谈闭包
闭包的定义
闭包就是能够读取其他函数内部变量的函数。
例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成定义在一个函数内部的函数
。
在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
为什么要使用闭包
先介绍一下全局变量和局部变量的优缺点
全局变量:可以重用、但是会造成全局污染而且容易被篡改。
局部变量:仅函数内使用不会造成全局污染也不会被篡改、不可以重用。
所以,全局变量和局部变量的优缺点刚好相对。闭包的出现正好结合了全局变量和局部变量的优点。
何时使用闭包
希望重用一个对象,但是又保护对象不被污染篡改时。
闭包产生的原因
官方解释:
闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
从这里可以看出闭包与环境有关,而与环境扯上关系就离不开作用域,然而JS作用域中特殊的就是词法作用域,这个词法作用域又称之为静态作用域或者闭包。
静态作用域就是函数声明时,就已经定好的作用域,以后也不会改变的作用域。
而JS的语言特性在JS代码运行的时候就已经把一切都定死了,作用域什么的都j定好了,闭包也随之而产生。
闭包是JS语言的一种特性,闭包通常是一个函数,函数是一个独立的作用域,独利的作用域外部环境无法访问,就是闭;封闭自己的词法作用域,函数有许多特殊形式的函数,这就成就了包。包的东西不同,但是它能够引用到外部函数的成员变量,一定是它包的东西。
可以理解为,能够引用外部函数的成员变量,那它就一定是闭包。
闭包的表现形式
- 返回一个函数
- 作为函数参数传递
- 回调函数
- 非典型闭包IIFE(立即执行函数表达式)
返回一个函数:这种形式的闭包在JS中非常非常常见。
1 | var a = 1; |
作为函数参数传递:无论通过何种手段将内部函数传递到它所在词法作用域之外,它都会持有对原始作用域的引用,无论在何处执行这个函数,都会产生闭包。
1 | var a=1; |
回调函数:在定时器、事件监听、Ajax请求、跨窗口通信、Web Workers或者任何异步中,只要使用了回调函数,实际上就是在使用闭包
1 | //定时器 |
IIFE: IIFE(立即执行函数表达式)并不是一个典型的闭包,但它确实创建了一个闭包。
1 | var a = 2; |
如何解决下面的循环输出问题
1 | for(var i = 1; i <= 5; i ++){ |
代码分析
for循环创建了5个定时器,并且定时器是在循环结束后才开始执行
for循环结束后,用var i定义的变量i此时等于6
依次执行五个定时器,都打印变量i,所以结果是打印5次6
第一种改进方法:利用IIFE(立即执行函数表达式)当每次for循环时,把此时的i变量传递到定时器中
1 | for(var i=1;i<=5;i++){ |
第二种改进方法: setTimeout函数的第三个参数,可以作为定时器执行时的变量进行使用
1 | for(var i=1;i<=5;i++){ |
第三种改进方法(推荐): 在循环中使用let i代替var i
1 | for(let i=1;i<=5;i++){ |