正常情况下外部变量无法访问函数内部的变量
如:
// 上级(外部)作用域的变量是无法访问下级(内部)作用域的变量
// 外部无法访问内部,内部无法访问外部==>这是JavaScript的作用域链
function fun(){
let num=10 // num是函数私有的
}
let test=num // test 拿不到函数内部的数据,除非函数主动提供
什么是闭包
闭包就是外部(全局)变量依赖于内函数,内函数又依赖于外函数,由于存在这种依赖关系,导致GC无法回收该外部函数,该外部函数就是一个闭包函数。
判断是否是闭包的核心要点,是判断这个外部变量或者内部函数能否脱离依赖单独存在
你看看下面哪个是闭包
function funA(){
let num=10
return num 没有中间商去传递依赖关系,没构成完整依赖链
}
let numA=funA()
function funB(){
let num=10
const obj={
num,
get() {
return num 没有脱离依赖,num是函数的
}}
return obj
}
let numB=funB()
...
numB=null // 手动回收funB
function funC(){
let msg='不是闭包'
return obj={msg} 脱离了依赖,msg是自己的
}
let numC=funC()
在判断是否是闭包之前,你需要知到哪些变量是引用类型,哪些变量是值类型。funA
和funB
都返回了一条数据,然而numA
存储的是funA
返回的值,funA
不是闭包,它执行完后就会被GC
回收掉;numB
存储的是funB
返回的地址(引用),funB
就是闭包,GC
无法回收,需要手动回收numB=null
,或者整个程序结束后,回收numB
时一起回收。
顺便提一嘴:全局变量是在程序结束之后被销毁,局部变量在没有形成闭包的情况下,代码块结束后就被销毁;如果形成了闭包,那么局部变量的生命周期与引用它的外部变量保持一致。
用一张图表示闭包,要想形成闭包需要完整的依赖关系,并且这个依赖关系需要持续存在
闭包的特点
- 函数作用域的变量是私有的,不允许直接修改
- 允许对外提供公共接口去修改,获取私有变量
- 由于闭包不会被回收(不会创建新的),它的私有变量会被一直持有(缓存)
const Module = (function moduleFun() {
let private_variable = 10 私有变量
const module = { 对外的访问接口
get() { 允许外部访问
return private_variable
},
set(val) { 允许外部修改
private_variable = val
},
add(count) { 缓存计算结果
private_variable += count
}
}
return module
})()
Module.get() //10
Module.set(29)
Module.get() //29
Module.add(10)
Module.get() // 39
Module.add(10)
Module.get() //49
闭包的用处
- 可以用来模拟类
- 可以用来模拟命名空间,当作模块使用
- 可以用来缓存数据的计算结果
出一个题
判断下面函数是不是闭包,并写出输出结果
提示:思考obj
内部的属性是自己的还是函数的
是闭包
function funA() {
let num = 10
const obj = {
num, 只有一瞬间的依赖关系
get() {
return num 依赖于函数的num是闭包
}
}
return obj
}
let numA = funA()
console.log(numA.num++) //10
console.log(numA.get()) //10
不是闭包
function funB() {
let num = 10
const obj = {
num, 只有一瞬间的依赖关系
get() {
return this.num 用的是它自己的num,不是闭包
}
}
return obj
}
let numB = funB()
console.log(numB.num++) //10
console.log(numB.get()) //11
不是闭包
function funOA() {
const user = { name: "hod", age: 22 }
const obj = {
user, 只有一瞬间的依赖关系
get() {
return this.user 使用的是自己的user,不依赖于函数
},
1,修改的是自己的user不是闭包。
2.由于函数和对象使用同一个user,这里被修改函数的user也会被修改
set() {
this.user.name = "lis"
}
}
return obj
}
let objA = funOA()
objA.set()
console.log(objA.get()) //{ name: 'lis', age: 22 }
是闭包
function funOB() {
const user = { name: "hod", age: 22 }
const obj = {
user,
get() {
return user 依赖于函数的user形成闭包
},
set() {
this.user.name = "lis"
}
}
return obj
}
let objB = funOB()
objB.set()
console.log(objB.get()) //{ name: 'lis', age: 22 }