阅读第三章。
变量的作用域和闭包
什么叫作用域
《js权威指南》:变量的作用域是指能够获取和操作某个变量的范围。
《js高级程序设计》:首先是执行环境的概念,或者叫执行上下文。(上下文根据sicp的描述,就是存放函数名和函数名对应的操作、变量名和变量名对应的值的地方。有了上下文,程序才知道“+”是什么操作,“nums”这个变量有什么值)。而作用域不仅仅是作用域,它叫作用域链,是每个执行环境都有的。通过作用域链,可以访问相对“全局”的变量。
全局作用域
怎样拥有全局作用域
在全局环境定义变量和函数,或定义变量时不加前置var。
特点
在任意作用域里都可以访问,并修改。
词法作用域
词法作用域就是静态作用域,实际上表达的是当各个作用域有同名变量时,解析这个名字到底会取到哪个值。
《js函数式编程》:一个变量的可见性。(这个说法很难懂)
《js权威指南》:全局变量会被局部变量遮盖(“遮盖”会把全局变量改掉吗,这个说法不是很好。)
《js高级程序设计》:还是作用域链,在解析标识符的时候,沿着作用域链一级级解析,解析到了,就不再往前了。(这是最好的解释。)
动态作用域
作者写的东西一开始完全看不懂,上网搜到了:https://github.com/mqyqingfeng/Blog/issues/3,才得到一点启发:
因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。
而与词法作用域相对的是动态作用域,函数的作用域是在函数调用的时候才决定的。
让我们认真看个例子就能明白之间的区别:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 > var value = 1;
>
> function foo() {
> console.log(value);
> }
>
> function bar() {
> var value = 2;
> foo();
> }
>
> bar();
>
> // 结果是 ???
>
假设JavaScript采用静态作用域,让我们分析下执行过程:
执行 foo 函数,先从 foo 函数内部查找是否有局部变量 value,如果没有,就根据书写的位置,查找上面一层的代码,也就是 value 等于 1,所以结果会打印 1。
假设JavaScript采用动态作用域,让我们分析下执行过程:
执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 value。如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 value 变量,所以结果会打印 2。
前面我们已经说了,JavaScript采用的是静态作用域,所以这个例子的结果是 1。
静态作用域和动态作用域的相同点在于:大家解析标识符的时候,都是用“作用域链”这个概念,逐级往上解析来弄的,这没区别。
不同点在于:这个“作用域链”,是什么时候定好的呢?
静态:一个函数定义的时候就定好了。所以看起来,这个函数的作用域链基本是不会变的。
动态:一个函数调用的时候才定好。如果这个函数被其他函数调用了,那么作用域链上要加入其他函数的作用域。
所以动态作用域是什么意思,到这里应该就清楚了。
至于作者写的那一票代码,是他对于“动态作用域”理解和实现的模拟。好像是比较高深的样子,暂时不理解也没关系。不过知乎上有个答案倒是给了一点启发:
动态作用域就是整个程序运行的时候只有一个env。
什么是env呢?env就是一组binding。
binding是什么呢?binding就是从identifer到value的映射。
dynamic scope在每次函数求值的时候都会在这唯一的一个env里查询或更新。
而static scope是每次函数求值的时候都创建一个新的env,包含了函数定义时候的所能访问到的各种binding。这个新的env连同那个函数一起,俗称闭包Closure。作者:韦冠楠链接:https://www.zhihu.com/question/20032419/answer/49183240来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
理解了吧,动态作用域就是一个很“穷”的实现,整个语言里只有一个地方(env)来放key和value的绑定,就好像以前穷得全家只有一条裤子,谁出去谁穿。那么姐姐穿出去的时候,口袋里放了3个铜板,等到弟弟穿出去的时候,再放1个铜板,那裤子里就有4个铜板了,谁让所有人都共享一条裤子呢。
所以为什么聊函数式的时候我们还要聊动态作用域呢?
javascript的动态作用域
javascript是静态作用域的,但是js也有动态作用域的入口,这就是this,以及function的call和apply方法。
它俩能动态地改变this的值。不过还好这种改变是“人为”的,并且所有人都知道call和apply的效果,所以大家心里都有潜台词:“不确定它会变成什么样,最好别碰”.
函数作用域
真的读不下去了,这个翻译读得头都烂了。
闭包
闭包和一等函数是唇齿相依的关系。
什么是闭包?
《js高级程序设计》:闭包是一个函数(function),在这个函数里使用了其他函数作用域里的变量。
《js函数式编程》:闭包是一个函数(function),这个函数直接使用了某些变量,而不是通过参数传入,这些变量的定义和这个函数的定义是在同一个作用域内。
按照《js函数式编程》的说法,使用全局变量的,在全局作用域内定义的,也叫闭包。
自由变量
自由变量和闭包有关联,这个关联是, 在闭包创建的时候,自由变量会被关进去。在函数里定义的变量,能被这个函数里定义的函数们获取到。这就是自由变量。
靠,作者自己举的代码例子和自己的解释背道而驰,然后代码例子也和大多数人的理解背道而驰,弃坑弃坑。读不完了。