在这个特别的日子里,容忍我叨叨一下:
年前最后一篇,提前祝大家新年快乐,恭喜发财,发大大大财!
做自己,相信一切美好,总在不期而遇。
什么是闭包:
what !!?
哈哈哈哈... 每次这个时候,我都会觉得自己是个憨憨,除了知道定义其他一无所知,悲伤一脸!
近期也是闲来无聊,看了一些资料,在这里结合案例和大家一起来分析分析:
案例一:
function test1(){
function test2() {
var b = 2
console.log(a) // 1
a = 'Hello World'
console.log(a) // Hello World
}
var a = 1
return test2
}
var c = 3
var test3 = test1()
test3()
案例一:结合预编译分析
-
预编译阶段,作用域指向:
-
预编译结束流程: A: test1 预编译结束,test1 断开 test1-AO 的连接
B:test3 预编译结束 —— test2 预编译结束:
tips:
- test3 执行后,test2 断开 test2-AO 的连接,test2-AO 被销毁
- test1 return test2 并赋值给了 test3, 此时 test2 一直保留保留着与 test1-AO 的连接,并死死拽着不放,因此 test1-AO 不会被销毁
C:由上图可以看出,test1-AO 一直在作用域链上,未被销毁,因此可以在 test2 中去访问 test1-AO 中的变量 a 的值,且为1
D:当我们改变 test1 中 a 的值为:Hello World,也就直接修改了 test1-AO 中变量 a 的值
案例二:
function outer() {
var num = 1
function inner() {
num++
console.log(num)
}
return inner
}
var addNum = outer()
addNum() // 2
addNum() // 3
addNum() // 4
案例二分析:
-
预编译过程:
-
预编译结束 - 作用域情况:
A:inner、outer 函数编译结束时作用域情况:
B:执行多个 addNum() 的数值变化过程
案例三:
function test() {
var str = 'Hello World'
var option = {
set: function(val) {
var num = 1
str = val
},
get: fucntion() {
var data = 2
console.log(str)
}
}
return option
}
let obj = test
obj.set('世界,你好!')
obj.get() // 世界,你好!
案例三分析:
-
预编译过程:
-
预编译结束- 销毁过程 A、test 执行结束:test 断开与 test-AO 之间的连接
B、option 对象被 test 函数 return 出来, option 中的 set、get 方法使用了 test-AO 中的变量 str, 因此option.get() 、option.set() 一直抓着 test-AO
C、赋值变化过程
- 执行完 test 之后,test 断开与 test-AO 之间的连接
- 执行完 obj.set('世界,你好!'), test-AO 中变量 str 被赋值为:世界,你好!
- obj.get(),获取 test-AO 中变化后 str 的值:世界,你好!
补充 return 、window
return 是赋值给了一个全局变量,然后在全局中执行
window.xxx:直接是全局变量,在全局中执行
示例:
return 的方式:
function test(){
var a = 1
function plus1() {
a++
console.log(a)
}
return plus1
}
var plus = test()
plus() // 2
plus() // 3
plus() // 4
window.xxx 的方式
function test() {
var a = 1
function plus1() {
a++
console.log(a)
}
window.plus1 = plus1()
}
var plus = test()
plus() // 2
plus() // 3
plus() // 4
总结:
- 当内部函数被返回到外部并保存时,一定会产生闭包
- 闭包会产生原来作用域链不释放
- 过渡的闭包会导致内存泄漏、加载过慢