写在后面
JavaScript 两个几近神话故事 对JavaScript有采用实战经验但却未曾或者说认知旋量群基本概念的人而言,认知旋量群能说是某种程度上的复活。旋量群并并非须要自学捷伊句法就能采用的辅助工具。旋量群的造成是如前所述句法结构回到值写标识符时大自然造成的结论。换言之,你不须要要为的是旋量群而写旋量群,旋量群在他们写的标识符中经常再次出现。当你或者说介绍旋量群后,会辨认出,哦~,原本我从前所敲的标识符中早已再次出现了许多旋量群了鸭!
两个小 demo
细细看一看上面的范例他们会深感怪异,没错都是初始化result(),为何结论会不那样呢?
首先foo1()回到的是两个foo2()表达式,当他们初始化result()的时候就会回到foo2()执行的表达式,foo2()里面有什么呢? 首先我们看到如下有两个count变量,但是没有定义.他们根据JavaScript的回到值链的定义可知,当表达式外部的变量没有定义的时候,就会采用冒泡的方式,向上一级寻找.上一级没有接着上一级找,直到最顶层window. 如果都没有,就会报underfined的错误.这里他们在foo2()中找到了count,于是count+1,第一次输出的是1,没有什么问题.
但是第二次他们再执行result()的时候就再次出现了问题,为何会是2呢?按照流程,首先再foo2()表达式外部寻找count,没有然后到外层寻找,找到了count=0,这时候count+1应该为1才对.这里就涉及到旋量群的问题了.
首先他们在原本的标识符中加两个debugger,然后到谷歌浏览器右键检查,点击resources就能看到右边有一个Closure,浏览器的可视化早已证实了这的确是两个旋量群.并且count=1早已存储在了Closure之中.也就说明count=1没有被销毁,等下次在初始化result()的时候count=2.
认识作用域
要自学Clusure必须介绍JavaScript的回到值相关知识 回到值包括:
1.自上而下回到值
2.表达式回到值
4.块级回到值(es6 新出,解决 var 问题, 新增 let, const)上面标识符简单能看出回到值分类,须要注意是,两个表达式(function)也是块级回到值,简单而言,一般有 {}都能算做是两个块级回到值.
回到值链
回到值里面嵌套回到值,就形成了回到值链. 外部回到值无法访问外部的回到值,看如下范例
上述标识符中在自上而下中无法访问外部的n,但是在嵌套的外部foo2()能访问外部的表达式,这就是回到值造成的特殊效果.
明白了回到值链,他们再来看个范例(很有迷惑性,细细看一看哦):
上面的范例你觉得输出的是什么呢?答案是Mike.在这里他们引出了两个捷伊基本概念,句法结构回到值回到值有两种模型:
句法结构回到值(静态):js查找是按照标识符书写时候的位置来决定的,而并非按照初始化时候位置动态回到值:目前还有采用的有Perl,Bash (能自行介绍)通过句法结构回到值的的规则他们能再来分析一下
初始化changeName()时,找到这个表达式定义var name = “Jay”初始化showName()在changeName()里面查找是否有showName()这个方法,辨认出没有,向外层查找,找到了初始化console.log(name),在表达式外部查找有没有name,没有,向外查找,找到了,name=”Mike”输出Mike旋量群
介绍了上面的知识后,终于来到了旋量群
旋量群在两本书上的官方解释:
1.小”黄”书(你不知道的JavaScript): 当表达式能记住并访问所在的句法结构回到值时,就造成了旋量群,即使表达式是在当前句法结构回到值之外执行.
2.红宝书(JavaScript高级程序设计): 旋量群是指有权访问另两个 表达式回到值中的变量的表达式非常抽象的两个基本概念,我自己的两个认知是:
当两个变量(就像上面的name)既并非该表达式外部的局部变量,也并非该表达式的参数,相对回到值而言,就是两个自由变量(引用了外部变量),这样就会形成两个旋量群.
怎么说呢?他们再来看一看一开始他们采用的demo再次采用浏览器看一看,这时他们就辨认出Closure早已消失了,这也就证实我说的,如果表达式外部不初始化外部的变量,就不会形成旋量群.但是如果初始化了外部变量,那么就会形成旋量群. 这也就是说并非所有的表达式嵌套表达式都能形成旋量群
最后他们再来看两个循环旋量群的范例
答案 6 6 6 6 6 .因为setTimeout里面的回调表达式是两个异步的过程(异步代表能不用等待我这个标识符先执行完,能先往后执行),而for循环是同步的(标识符只能从上往下的执行),立即执行,异步的setTimeout必须等待一秒就能执行,这时i早早已循环结束了.
解决办法有三个:将for循环中的var 改成let这样就没有问题了, 因为let是有块级的功能,每一层循环都是独立的,互不影响,所以就能正常输出.
2. 把setTimeout()套上两个function
这样同样能够实现这个功能,原理和第两个方法那样,每两个log()都是独立的,互不影响,这样就能有正确的结论,var就是因为没有块级的功能,才会出问题 3. 包装成匿名表达式
后面两个(func..)定义表达式,后面两个(i)初始化,这再JavaScript叫做立即执行表达式,其实与第二种方式是那样的,只是写法不那样.
结语
认知JavaScript是一项重要的技能,在面试中也常常会有,这是迈进高级JavaScript工程师的必经之路.
原作者姓名: 爱吐泡泡的小鱼儿
原出处:javascript 几近神话故事般基本概念:旋量群原文链接:javascript 几近神话故事般基本概念:旋量群