读you don't know js 有感之作用域

来源:互联网 发布:oa 网络地板 线槽 编辑:程序博客网 时间:2024/06/05 08:46

作用域这个词有过一些编程经验的猿们应该都不陌生,可是深究下去,他到底是什么呢?

根据本书所述我的理解是:作用域是在程序中储存变量值和引用的一套规则。但是深究下去,这套规则到底是在哪储存变量值和引用?使用时如何取得它们?甚至这套规则是如何设置的呢?

通常我们总是遇到js是动态执行语言这种说法,但是实际上js也是一门编译语言,但是与传统的编译语言不同,js不是提前编译的,编译结果也不能在分布式系统中进行移植。笼统来说的话,js的编译大概分为三步,分词/词法分析,解析/语法分析,代码生成。

大概解释一下这三步的内容,语法分析是将字符组成的字符串分解成有意义的代码块,或者可以称其为词法单元,通俗说就是对我们写的代码进行初步加工,分解成方便语法分析的代码块等,当然其中还进行一些更加高层次的事项包括一系列特定的性能优化等步骤,但超出我们的内容范围就不做深究了;紧接着编译器会对词法单元进行检查是否符合ECMA标准有没有type、reference等错误,如果有的话就报错,同时将其变成“抽象语法树”,可以理解为一个代码单元tree;最后将抽象语法树转换为引擎可以直接执行的代码,另外可能还有很多像性能优化、语义分析等的过程才会执行代码。简单来说,整个编译过程就是对代码进行一系列变化,为后来执行做好准备。

好了,跑题了一会,现在让我们回到作用域的讨论上,当然编译过程其实和我们对作用域还有引擎的理解有很大关系,作用域在编译过程中要配合编译器工作,由于js是解释型语言,并没有提前编译,所以你编写的js代码在浏览器上的行为其实是非常快速的编译,然后直接执行,而这个过程的时间与用户体验有极大的关联,所以引擎一般都会被设计的尽量迅速来保证性能最佳。然后让我们来看一下引擎、作用域和编译到底处在是一个什么样的运行机制。

首先先介绍一下这三者。

引擎:负责整个js程序的编译以及执行过程。

编译器:负责编译过程,包括词法分析,语法分析以及代码分析等工作。

作用域:负责收集并维护所有声明的标识符组成的一系列查询,其遵循着一套严格的规则,确定着代码访问修改等一系列行为的权限。

接下来简单分析一下

 var a = 2;
过程中所发生的“爱恨情仇”。首先编译器进行工作,遇见var a,编译器会向作用域询问是否在本作用域已经声明过a变量(LHS查询),如果不在严格模式下且声明过的话,那么编译器会忽略var,如果没声明过那么编译器会要求在作用域中声明一个新变量a;其次编译过程完毕后,编译器为引擎提供了供执行用的代码,引擎会执行 a = 2; 引擎会向作用域询问是否本作用域存在变量a,如果存在那么就取得他的引用(RHS查询),如果不存在引擎会继续查找(后文会解释)该变量。最终如果查到了就进行赋值操作,没查到就会报出异常。

其实总结下来 var a = 2; 这段程序主要就是两步操作,LHS和RHS查询,为了理解作用域的机制,我们还是有必要深入探究一下这两个编译术语。简单来说LHS就是取得值的引用,RHS就是取值,为了方便记忆可以认为LHS多是为了修改变量,RHS多是为了利用变量值。

这样下来应该就可以初步理解一些作用域的问题了,接下来我们更深入的讨论一个作用域的嵌套问题。还记得前面有个遗留的问题,如何继续查找呢?

当一个块或函数嵌套在另一个块或函数中时就产生了作用域嵌套。在当前作用域中如果无法找到变量,那么引擎会在上一级作用域中继续寻找,直到找到该变量或者最外层作用域为止。


0 0