Js闭包的原理(图解)

JavaScript08

Js闭包的原理(图解),第1张

什么是闭包(Closure)?

网上流传各种说法,在Javascript语言中,我的理解是: 保存着其他函数内部变量的函数,就是闭包。

挺绕的,但不虚,让我们一步步揭开它的神秘面纱!

要理解闭包,我们得先搞清楚以下几个概念:

JS的作用域分两种:全局作用域、局部作用域(也可称为函数作用域)

总的来说,Js作用域的一般机制就是:内部可访问外部的变量,外部无法访问内部的变量。

那么这套作用域机制是如何实现的呢?答案是:通过作用域链

在Js中,每当一个函数被执行,都会产生三个对象:

我们通过实例配图讲解,例如有如下 js 文件:

当浏览器运行解析 example.js 后,首先创建了全局执行环境 (Window 对象)、Window 作用域链和 Global 全局活动对象,如图:

搞明白了作用域链,离弄清楚什么是闭包就仅一步之遥了! 我们来看看下面这个实例:

当执行 var func = outer() 时,情况如图:

接下来,当执行 console.log(func()) 时, 情况如图:

学习记录,别提问,我可能答不上来!^-^。。。

1.预编译

我们的需求是希望输出0-9 依次输出 0 1 2 3 4 5 6 7 8 9 ,但是这个输出的是十个10;

为什么是十个10呢,因为test()函数内循环时,arr[i]是被赋值为一个函数体,该函数体并没有执行,所以console.log(i)中的i并没有随着arr[i]中的i一起改变,然后被保存到外部myArr,myArr就是一个数组,里面有10个函数,myArr[j]()就是来调用myArr中的函数运行。当myArr[j]()函数运行console.log(i)时,这个i访问的是test()的AO。

上文说到arr[i]并没有运行,test()中for循环i=10时不满足循环条件,退出循环,这时 test()的AO对象中i就等于10了,在test()函数结束之前,arr被返回到函数外部,这个myArr就继承了test() 的AO对象。至此test()执行完毕,释放AO对象。重点来了,test()的AO 对象释放了,但是!myArr在test()结束之前继承了test()的AO对象,所以test()的AO对象被myArr拿到了手中,没想到吧!!所以在myArr循环输出console.log(i)时,这个i实际输出的是test()AO中的i,这个i在test()循环结束时等于10,所以外部循环调用输出console.log(i),循环10次,输出的就是10个10。好晕!看图:

匿名函数自调会立即执行,让i作为实参,j为形参,函数声明执行前都会预编译生成AO, 形参实参统一,每执行一次形参j都会跟实参 i 改变;循环结束后arr中就相当于放了10个匿名函数自调,并引用了匿名函数自调的AO,每一个AO中j都根据i而累加了,所以在外部调用输出j时,就再这个匿名函数自调中找j,输出就达到我们要的效果啦。下图是arr中匿名函数的AO,每一个j都根据i改变:

至此就完美的解决闭包问题啦!!

学习记录,有写错的地方可以指出改正^-^