简介
本文是我在阅读书籍《你不知道的JavaScript-上卷》时的总结和看法。主要是对一些重要知识点的梳理和归纳,并用自己的思路去进行书写成记录来便于日后的回顾和加深知识点的认识,所描述内容仅供学习参考。
作用域以及作用域链
作用域
以前一直不知道什么是作用域,或者说只知道作用域,而不知道他实际代表的含义。
作用域实际是浏览器引擎在查询变量的一套规则。按我的理解,作用域存储了已经被声明的变量,以及这些被声明的变量所代表的值,当引擎在进行 LHS 或者 RHS 查询的时候,就需要到对应的作用域进行查询。
LHS:查询某个变量是否声明过
RHS:查询某个变量所代表的值
比如下面这段代码:
var a = 0 // 对 a 变量进行 LHS 查询
var b = a // 对 b 变量进行 LHS 查询,然后对 a 进行 RHS 查询,找到 a 代表的值并复制给 b
这里不详细赘述 LHS 和 RHS,这不是我们的重点
用一个简单的示例来更加好的理解作用域:
将作用域比作是一个密闭的房间,当存在多个作用域时,便相当于有多个房间。房间之间是互不相通(不考虑作用域嵌套或其他情况)。在作用域内声明的变量名相当于是对房间内某个物品的别名,变量的值则是所代表的物品。比如 a 代表了苹果,b 代表了椅子,这里是比喻。每个房间内相同的别名可能代表了不同的物品,也就是说,每个房间内的别名和所代表的事物是相互独立,不受影响的(不考虑作用域嵌套)。同时也无法知道别的房间内所定义的别名和该别名多代表的事物。
好比现在有两个房间 A,B
当我们处于 A 房间时,我们可以清楚的知道 a 代表了椅子,b 代表了苹果。但我无法知道 B 房间内是否有 a 以及 a 代表的含义。因为 A B 是相互独立的,所以我们无法在 A 房间内观察到 B 房间内的情况(不考虑特殊情况)。
如果用 JavaScript 来描述的话:
function A () { var a = '椅子' var b = '苹果' ... } function B () { var a = '桌子' var b = '收音机' ... }
上述的情况是基于一般情况。在某些特殊情况下,是有机会访问这些独立房间内的成员。这个我们后面会讲到。
而且对于作用域而言,只有在代码执行到对应的地方时,才会在内存开辟一定的空间来生成这些房间(作用域),当代码执行完毕后,这些房间将会被回收机制所回收(不考虑闭包的情况)。
作用域嵌套
作用域链实际是作用域嵌套所形成的一个现象。作用域有一个非常特殊的性质,作用域外部无法访问作用域内部的成员变量,但作用域内部却可以访问作用域外部的成员变量,并且还可以访问上一级作用域的更上一级作用域的成员变量。
对于嵌套的作用域,浏览器引擎在进行变量查询(LHS 和 RHS)的时候,如果无法在当前作用域查询到对应的变量名,会尝试到上一级的作用域去查询该变量,若还是查不到,会到上上以及作用域查询…,以此类推,直到找到有效的成员变量或到达最顶层的作用域——全局作用域,最终结束查询。这种一层层作用域嵌套的现象,形成了作用域链。若在词法作用域解析的阶段,无法在当前作用域链查询到有效的变量声明,引擎会抛出一个错误 Uncaught ReferenceError: a is not defined。
从上面的描述我们可以知道,引擎对于某个变量的查询是依照当前的作用域链进行查找的,这个机制可以让我们利用外层作用域的变量,但也会带来不必要的影响。这种查询机制在作用域链太长的情况下会消耗大量的时间和资源来查找变量。这同样是我们需要尽量避免的。
同样的,我们还是用一个上面的房间的示例来简单描述一下
这里有三个房间 A B C,C 房间嵌套在 A 内,A 与 B相互独立
对于 C 房间而言,当他想找到 b 成员却找不到时:
- C 可以向 A 询问:A 你有没有看到 b,我想知道他是什么
- A 说:没问题,我有,他是苹果
- 但如果 A 里面也没有,A 会向他的上一级进行这样的询问,得到答案后会将答案给到 C,以此类推(这里只是隐喻他们查询的过程以及关系,不代表真正的执行过程)
但若是 A 找不到 d,想询问 C 时:
- A 询问 C:C 你有没有看到 d,我想知道他是什么
- C 说:抱歉,我不能告诉你,你不配!你找你上一级看看。
- A 说:我…,行吧,那我找其他人看看吧
如果用 JavaScript 来描述的话:
function A () { var a = '椅子' var b = '苹果' console.log(d) // 我找不到 function C () { var a = '鸭梨' console.log(b) // 苹果 (忽略前面的报错) } ... } function B () { var a = '桌子' var b = '收音机' ... }
我想表达的意思是。对于嵌套的作用域,在执行代码的时候,引擎会先在当前的所在作用域去查找需要的成员变量,如果找不到,会尝试向上一级的作用域去查找 …,一直到查询成功或到达顶层作用域。所以对于作用域链中重名的成员变量,最靠近执行时作用域的享有优先权。同时这种作用域嵌套的规则最终形成了作用域链,以及作用域链查询的这种机制或者规则
下一节,我们会来讨论一种可以 “违反” 这种查询机制的现象——闭包


208

被折叠的 条评论
为什么被折叠?



