读了这篇博文地址后,对做用域和闭包终于有一了些了解。以前看各类文章,让我觉得闭包是由于内部引用变量,致使变量没法在外部访问,而经过内部函数能够被外部访问致使被引用的变量能够间接访问。如今看来,这只能说是闭包的一种外在表现,跟闭包自己没有任何关系的(在You don't know JS中理解与本文不一致,书中认为闭包其实是做用域,因为内部函数引用致使外部函数在栈中移出后其做用域仍可被访问,这个做用域被称之为闭包。这样理解的话本文查看的应该是chrome的一种实现,即无论有没有闭包,都会生成一个叫closure的变量,这个变量就是做用域,只是chrome进行了优化,便于垃圾回收,移除了部分非使用的变量。)。html
下面总结一下做用域链和闭包。git
更为详细的可参见那篇博文,这里只是简单总结下。在浏览器执行解析代码时,分为编译和代码执行两个部分。编译阶段负责对语法解析,做用域链分析,语法优化。代码执行阶段中,先为函数建立上下文,而后执行代码。建立函数上下文时,要进行:建立变量对象、建立做用域链、肯定this的指向。github
变量对象(VO)建立过程:创建arguments对象,并将数据挂载到到arguments上 -> 针对函数内部声明的函数、变量,先进行挂载声明的函数,而后挂载声明的变量(若是名称冲突则不覆盖) 面试
变量对象在函数执行以前是不可访问的,执行代码时,变量对象转变为活动对象,全部变量获取均从活动对象中取得,若是没有,则会抛异常。chrome
做用域链的生成规则是在编译阶段就肯定了的。当函数建立上下文时,除建立变量对象外,还会产生一个闭包(closure)。这个闭包会在子函数中被加入到做用域链中。某个函数的做用域链是这个样子的:浏览器
当前函数上下文的活动变量 - 上层闭包 - 上上层闭包 .....闭包
所以闭包的概念应该是这样的:在某个函数内声明了另一个函数(子函数),子函数(或子函数的子函数)引用了当前函数声明的一些变量或函数,这些变量或函数因为在子函数(或子函数的子函数)的做用域中会被访问到,所以这些变量或函数的访问地址被保存到了一个对象中,这个对象就是一个闭包。函数
闭包的实际做用应该为子函数的函数做用域链提供一个可访问的对象,即便当前的函数已经从栈中移出,因为子函数中已经将该闭包加到了做用域链中(子函数的子函数可继承使用子函数的全部闭包),子函数全部的变量或函数都是从做用域链中获取的,所以子函数从表现上看仍旧能够访问在父函数或父父函数中声明的变量(实际上访问的是做用域链上的变量)。优化
这样,函数的做用域链的规则应该是这样的:this
当前函数上下文的活动变量 - 上层函数的闭包 - ...上层函数拥有的闭包
这样一看就清晰明了了吧。
为验证这个推论,创建一个带有四层函数的脚本。其中第一层中声明变量a,被second、four引用, cuse变量仅被four引用,unuse未被任何子函数引用。
(function () { var a = 1
var cuse = 2
var unuse = 10 function second () { var b = a function third() { var d = 'xx' function four () { var c = b c += a c += cuse console.log(c) } four() console.log(d) } third() } second() var final = unuse + 1
return final })()
1. 执行到second时,看到匿名函数的closure中只有a和cuse,但second中未引用匿名函数声明的任何一个变量,说明这个closure应该是在编译阶段就肯定了哪些变量会被加入到closure中。
2. 执行到third函数时,closure中出现了匿名函数的closure和second的closure。
3. 执行到four时,出现了匿名函数的closure和second的closure。因为third中声明的变量没有被子函数引用,它未产生closure,在four中也就没有出现closure。
从上面的结果看出:编译阶段已经确认了哪些变量会被加入到closure中,这个closure会被加入到全部子函数的做用域链中。换种理解就是子函数的使用域链中closure构成是由上层函数生成的closure以及它所拥有的全部closure组成的。
当使用eval函数时,会有更明显的对比(见下图):使用eval后,父函数声明的全部变量和函数都被加入到closure中。这是由于eval执行具备不肯定性,为保证变量能够访问,只能将全部变量加入closure中,避免代码执行过程当中找不到变量的问题(arguments之因此被加入到closure中,是由于新的箭头函数致使子函数会使用父函数的arguments对象)。
闭包的概念仍是比较难以理解的,要从浏览器解释执行代码的角度理解才会更深入(貌似这不是写代码的考虑的,不知道为啥面试全会问)。感谢·这波能反杀·博主的详细讲解,一对比文章质量,我写的估计只有理解的人能快速看明白,有点像写论文,但写的匆忙质量差,仍是要多练习。继续加油吧~~
附: 博文地址 https://yangbo5207.github.io/wutongluo/ji-chu-jin-jie-xi-lie/si-3001-zuo-yong-yu-lian-yu-bi-bao.html