一.理解闭包
1.闭包的定义
当一个嵌套的内部(子)函数引用了外部(父)函数的变量(函数)时,其中包含的变量不会随着外部函数的释放而跟着销毁,会暂时保存在栈中,此时包含了被引用变量(函数)的对象就叫做闭包.
2.常见的闭包
a.
function fn1() {
var a = 1
function fn2() {
console.log(a);
}
}
fn1()
其中a是fn1中的变量,在fn2被调用.
b.
function showDelay(msg,time){
setTimeout(
function(){
alert(msg);
},time)
}
showDelay('hello',2000)//两秒后弹出hello
计时器中的函数作为内部函数调用了外部函数showdelay的参数msg,msg在闭包中,而time不在(time不是被内部函数调用的) .
3.产生闭包的条件
1.函数的嵌套
2.内部函数引用了外部函数的变量
4.闭包的生命周期
产生:嵌套的内部函数的定义执行完毕时
死亡:嵌套的内部函数成为垃圾对象时
function fn1(){
// 闭包产生(函数提升)
var a=1
function fn2(){
console.log(++a);
}
return fn2
}
var f =fn1()//将fn2返回给f
f()//2
f()//3
f=null
//闭包死亡(包含闭包的函数成为垃圾对象,再也不能被调用)
二.闭包的作用以及应用
该段代码体现了闭包的两个作用.
function fn1(){
var a=1
function fn2(){
console.log(++a);
}
return fn2
}
var f =fn1()//将fn2返回给f
f()//2
f()//3
闭包的两个作用:
1.使函数内部的变量在函数执行完后们仍然存在内存中(延长了局部变量的声明周期)
a在fn1调用完毕后并没有随之消失,被存在了闭包中,所以a仍然能够随着f()的调用而递增.
2.让函数外部能直接访问函数内部的数据(变量/函数)
原本外部函数不能直接访问内部函数中的局部变量,但是因为将fn2赋给了f,此时a就可以被外部函数直接访问.
闭包的应用
1.自定义js模块
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="myModule.js"></script>
<script>
myModule.uper()
myModule.lower()
</script>
</body>
</html>
引用的js代码:
(function () {
// 私有的数据
var str = 'HeLloWoRlD'
// 操作数据的函数
function uper() {
console.log(str.toUpperCase());
}
function lower() {
console.log(str.toLowerCase());
}
// 将其添加在window中
window.myModule = {
uper,
lower
}
})()
此时我们将所有的数据和功能封装在了一个函数内部(私有的),并且只向外暴露一个包含这两个方法的对象,模块的使用者,只需要也只能通过对模块暴露的对象去调用方法来实现对应的功能.
2.解决全局作用域的问题
要求:按按钮输出对应的数字.
<body>
<button>1</button>
<button>2</button>
<button>3</button>
</body>
var btns = document.getElementsByTagName('button')
for (var i = 0,length=btns.length; i < length; i++) {
var btn=btns[i]
btn.onclick=function(){
alert(`第${i+1}个`)
}
}
如果我们直接对btns进行循环遍历,那么按下按钮时会发现输出的全都是4,而不是我们要的对应的123,这是因为i时用var定义的,导致该段代码中只有一个全局变量i,而函数是在循环结束后才被调用,则i此时为3,所以所有的按钮都只会提示4.
我们可以用闭包解决这个作用域的问题
var btns = document.getElementsByTagName('button')
for (var i = 0, length = btns.length; i < length; i++) {
(function (i) {
var btn = btns[i]
btn.onclick = function () {
alert(`第${i + 1}个`)
}
})(i)
}
利用匿名函数的自调用,这时我们将对应的i都保存在了对应的闭包中,这样每一个点击事件的i值都不相同,保存在闭包中的i值就是对应需要的值
三.闭包的缺点以及解决方法
缺点:
函数执行后,函数背的局部变量没有释放,占用内存时间过长,造成内存泄漏
解决方法:
及时进行释放
function fn1(){
var a=1
function fn2(){
console.log(a);
}
return fn2
}
var f=fn1()
f()
f=null
我们使内部函数变成了垃圾对象,此时闭包死亡,占用的内存就会释放