什么是闭包,其实闭包是能够重用一个对象,又保护对象不被篡改的一种机制。什么是重用一个对象又保护其不被篡改呢?请看下面的详解。windows
注意
理解做用域和做用域链对理解闭包有很是大的帮助,因此咱们先说一下做用域和做用域链闭包
什么是做用域
做用域表示的是一个变量的可用范围、其实它是一个保存变量的对象
为何要使用做用域
避免不一样范围的变量互相干扰函数
做用域包含了哪两种
一、全局做用域
在JavaScript中的全局做用域其实就是windows
优势:可重复使用,随处可用
缺点:会形成全局污染学习
二、函数做用域
临时建立的活动对象AO(Activation Object)、该对象包含了函数的全部局部变量、命名参数、参数集合以及this,当运行时上下文被销毁、活动也会被销毁(闭包造成的缘由其实由于就是由于活动对象被引用着没法被销毁而致使的,详细的请继续往下看)
优势:不污染全局
缺点:不可重复使用、仅在函数内可使用this
程序执行的四个阶段
我如下面一段代码解释一下程序执行的几个阶段spa
var age = "21"; function myAge(){ var age = 0; age++; console.log(age); } myAge(); console.log(age);
第一阶段:在内存中建立执行执行环境栈、把全局对象window压入栈底、在window中声明变量code
第二阶段:函数调用时
在执行环境中添加当前函数调用、为本次函数调用建立活动对象AO、根据scope指定运行期活动对象AO的上下文内部对象对象
第三阶段:函数调用后
函数调用从执行环境栈中出栈、函数做用域AO释放、函数做用域AO中的局部变量也一同被释放图片
由上面能够看出当一个函数调用完毕它的局部变量就会被释放,下次再次调用时会建立新的局部变量。这就是函数中的局部变量不可重用的缘由。ip
先介绍一下全局变量和局部变量的优缺点
全局变量:能够重用、可是会形成全局污染并且容易被篡改
局部变量:仅函数内使用不会形成全局污染也不会被篡改、不能够重用
从上面能够看出全局变量和局部变量的优缺点恰好是相对的。闭包的出现正好结合了全局变量和局部变量的优势。
什么时候使用闭包
但愿重用一个对象,又保护对象不被污染篡改时
弄清楚了做用域和做用域链、闭包实现的原理也就很容易弄懂了。
下面请看一段代码和几张图^_^
这是一段闭包的代码,咱们又这段代码讲讲闭包的原理
function addAge(){ var age = 21; return function(){ age++; console.log(age); } } var clourse = addAge(); clourse(); clourse(); clourse();
第一阶段:在内存中建立执行执行环境栈、把全局对象window压入栈底、在window中声明变量(和前面是类似的)
第二阶段:
一、在栈中添加addAge的函数调用
二、为addAge函数建立活动对象AO、根据addAge函数的scope能够知道其活动对象指向window
三、window对象中的clourse变量记录着addAge()返回的匿名函数的地址[如今addAge()和clourse变量均可以找到匿名函数和addAge()产生的AO]
第三阶段:
addAge()调用完毕出栈、其对活动对象AO的引用也随之消失。
在做用域和做用域链中举的例子中,活动对象AO会被JS中的垃圾回收机制回收你们还记得嘛^_^,
可是这里和前面是不同的哦!注意了:匿名函数中的scope引用着活动对象AO、匿名函数的地址也被clourse变量记录着。所以,addAge()虽然出栈了,对它的活动对象的引用也消失了,可是其活动对象被匿名函数的scope拽着、因此没法释放不会被回收。
你们观看蓝色的箭头,其实能够发现、蓝色的箭头已经造成了一个闭环了。
此时,由图也能够看出,活动对象AO只能经过clourse变量来找到。这里造成了一个闭包。保存了addAge()函数中的局部变量,使其能够重复使用,可是又不会形成全局污染。这就是闭包的一个使用场景:保存现场。
至于怎么调用重复使用局部变量,具体过程请看下面两幅图。
第四阶段:
clourse()进栈,产生clouse()的活动对象AO,根据它的scope能够知道它的__parent__指向addAge()产生的活动对象AO。
clouse()执行age++,因为在它本身的做用域里面没有age、因而它会到上一级做用域查找age,它在它的上一级做用域中找到了age,因而对其进行了age++,age从21变成了22。执行console.log(age)输出22。
第五阶段:
clourse()出栈,由于clourse产生的AO没有scope拽着它,所以clourse的AO是能够正常释放的。函数出栈,其AO被JS的垃圾回收机制回收。
clourse变量中的匿名函数中的scope依旧拽着addAge()产生的活动对象AO,因而这个活动对象依旧没法被释放[并且这个AO如今只能被clourse找到、clourse能够重复使用这个AO里面的局部变量age、又不会形成全局污染]
剩下阶段:
代码中还有两个函数还有执行
clourse() clourse()
它的执行和第四阶段和第五阶段的原理是同样的。因此在这里我就不重复画图了。
执行clourse():看第四阶段的图,age从22变成了23,执行console.log(age)输出2三、看第五阶段的图
执行clourse(): 看第四阶段的图,age从23变成了24,执行console.log(age)输出2四、看第五阶段的图
以上就是对闭包造成过程的图解。也说明了闭包保存现场的做用场景。闭包结合了局部变量和全局变量的优势。可使变量不污染全局,可是又能对变量进行重用。可是,其实闭包也有有缺点的,它比起普通函数会占用更多的内存。总的来讲,以上就是我对闭包的理解。若是你们发现了什么错误欢迎评论指出,一块儿交流一块儿学习一块儿进步!^_^