一、为什么使用闭包
局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。闭包就实现了上述的功能,闭包使得函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期),让函数外部可以操作(读写)到函数内部的数据(变量/函数)。闭包存在的意义就是让我们可以间接访问函数内部的变量
二、闭包的创建
定义:函数 a
内部有一个函数b,函数 b
可以访问到函数a中的变量,那么函数b就是闭包
当一个内部函数【b()】被其外部函数【a()】之外的变量【c】引用时,就形成了一个闭包。创建闭包的最常见的方式就是在一个函数【a()】内创建另一个函数【b()】,通过另一个函数【c()】访问这个函数【a()】的局部变量。
创建规则:函数嵌套;内部函数引用了外部函数的数据(变量/函数)。
三、闭包三个特性:
(1)函数嵌套函数
(2)函数内部可以引用外部的参数和变量
(3)参数和变量不会被垃圾回收机制回收
四、闭包有什么用,使用场景。
使用闭包主要是为了设计私有的方法和变量,闭包的优点是可以避免全局变量的污染。
当我们需要在模块中定义一些变量,并希望这些变量一直保存在内存中但又不会“污染”全局的变量时,就可以用闭包来定义这个模块。
五、闭包的缺点
闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
六、函数套函数就是闭包吗?
不是!,当一个内部函数被其外部函数之外的变量引用时,才会形成了一个闭包。
七、常见的闭包使用形式:
1. 将函数作为另一个函数的返回值,2. 将函数的形参作为实参传递给另一个函数调用
八、闭包的生命周期:
产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
死亡: 在嵌套的内部函数成为垃圾对象时
九、闭包应用:
(一)同步和异步:借助闭包, 把每次循环的i值都封闭起来; 当在事件函数中顺着作用域链中从内到外查找变量i, 会先找到被封闭在闭包环境中的i
经典面试题,循环中使用闭包解决 var
定义函数的问题setTimeout
是个异步函数,所以会先把循环全部执行完毕,这时候 i
就是 6
了,所以会5个 6
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
解决方法:
(1)用闭包的方式:使用了立即执行函数将 i
传入函数内部,这个时候值就被固定在了参数 j
上面不会改变,当下次执行 timer
这个闭包的时候,就可以使用外部函数的变量 j
,从而达到目的。
for (var i = 1; i <= 5; i++) {
;(function(j) {
setTimeout(function timer() {
console.log(j)
}, j * 1000)
})(i)
}
(2)使用 setTimeout
的第三个参数,这个参数会被当成 timer
函数的参数传入
for (var i = 1; i <= 5; i++) {
setTimeout(
function timer(j) {
console.log(j)
},
i * 1000,
i
)
}
(3)使用 let
定义 i
,最推荐的方式
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
(二)封装全局变量
作用域链条:JS中有很多作用域, 比如: 全局作用域 和 局部作用域
1. 凡是存在作用域的地方一定有作用域链条, 变量的查找都是沿着这条链条自内而外的;
2. 寻找变量都是递归遍历寻找, 当前作用域找不到, 就跳到上一个作用域遍历寻找, 直至顶层;
3. 作用域链条太长, 会影响程序运行效率
把一些不需要暴露在全局的变量封装成"私有变量"
(三)定义JS模块
将所有的数据和功能都封装在一个函数内部(私有的);只向外暴露一个包含多个方法的对象或函数;模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
学习过程中,发现这篇文章写的深入人心,分享给大家 javascript中闭包的工作原理 - Frontopen
共同努力~希望自己的技术更上一层楼。