JavaScript基础–作用域
2016-01-06 14:37
330 查看
JavaScript基础–作用域
什么是作用域
作用域定义了变量在哪查找和怎样查找的一系列规则。通常我们在JS中定义的变量具有一定调用范围,比如全局变量可以在任意位置调用;而这个范围通常就是我们所指的作用域,JS中的作用域不同于C/C++,那么有哪些不同呢,请先和我一起了解一下JS简单的编译原理编译原理
JS虽然是一门脚本语言,但是事实上它也是需要编译的(ps.通常脚本语言只需要“解释”,而不是“编译”)。但与其他传统的编译语言(例如java)不同的是,JS并不会提前编译好代码等待执行,而是很快被编译,然后再迅速执行(通常在几微秒以内)。即使有这样的差异,实际上它们的大致的编辑过程差不多,主要分为三个阶段:1.分词/词法分析
分词就是将表达式进行拆分,比如
vara=5;,编译引擎会将其分解为
var,
a,
=,
5和
;。词法分析与分词差不多,它的作用是分析表达式中各个组成部分的意思,类似于一个句子中什么是主语、谓语等。
2.解析代码
第二步,解析代码。在这个过程中会生成一个抽象语法树(AST,AbstractSyntaxTree),这个树上嵌套了第一步分解的各个“小元件”。还是以上面的表达式为例子,
vara=5;,这时根节点为
var,它有两个子节点,一个是
a,一个是
=,
=下面还有一个子节点
5。
3.代码生成
这个过程生成可以执行的代码,是面向机器的。这个过程中机器开始给变量分配内存,构建作用域等等。
理解作用域
JS代码在编译和执行的整个过程中,有三个“人物”扮演了重要的角色,分别是引擎、编译器和作用域。怎么理解这三个“人物”呢?引擎相当于这三者中的老大,负责JS代码的编译和执行;编译器负责解析代码、代码生成等;作用域负责管理代码中各个变量应该活动的范围。三人各司其职,保障JS在平台上的顺利运行。工作流程
引擎、编译器和作用器各自承担成自己的责任,那么三者是如何协调工作的呢?以vara=5;为例。
1.首先编译阶段,编译器会询问作用域那边是否有变量
a,如果有,则编译器忽略这句声明,如果没有,则在作用域中生成一个变量
a;
2.在代码生成过程中,编译器就将
5赋给变量
a;
3.最后在引擎在执行代码过程中,也会先问作用域哪里有没有
a,有就取它的值来用,如果没有,就去别的地方找(具体在哪里,继续往下看)。
查找方式
引擎在执行代码的过程中,比如vara=5;,很多书和博客将会搬出两个概念:左查找(LHS)和右查找(RHS),然后又解释了很多,其实左查找就是查找变量,右查找就是查找变量的值,如果这样理解的话,你就很容易知道什么时候用的左查找,什么时候用的右查找。
后来我自己研究了一下,发现左查找和右查找的唯一作用是标识引擎查找返回错误,如果找不到变量,就报
ReferenceError,找不到变量的值(属性),就报
TypeError。如果还有其他作用,欢迎补充。
嵌套作用域
嵌套作用域比较好理解,不过这里要注意两点:一个认识到JS是函数作用域,作用域以函数为单位来嵌套的;第二个是区分嵌套作用域与闭包的不同。上面的代码中就是一个简单的嵌套作用域的例子,全局定义了
b,在函数里定义了
a,被嵌套在全局作用域里的函数
foo可以调用
b,但全局作用域里不能调用
a。
引擎在查找变量时,是先从所执行的代码的作用域里开始查找的,如果找不到,再一级一级往外层的作用域里查找。
进一步理解作用域
在编译的阶段,作用域就形成,在引擎执行代码时,作用域的规则就开始限定引擎要怎么取变量或者值了。我们举个例子。在上面的例子中,嵌套了三层作用域,第一层是全局作用域,第二层是函数
foo的作用域,第三层是函数
bar的作用域。引擎在执行某段代码时,查找的规则是这样的:
首先在当下的作用域里找,找的到就直接调用,找不到就想上一层作用域继续找,再找不到就再往上,直到全局作用域;
如果在当下的作用域下定义了一个与外层作用域相同的变量,以当下的作用域的为准。
在第一条规则中,定义变量时,需要在变量的前面加上
var,如果不加,在非严格的模式下,会在全局作用域创建这个变量,在严格模式下会报错。
在第二条规则中,如果在当下作用域中依然想调用全局那个重复的变量,可以这样定义
window.a=5;,调用的时候直接去
window.a就可以了。
注:调用
with和
eval()改变执行环境作用域并不推荐使用,因为它们会带来性能问题,这里不详细解释,大家可以参考文章后面参考文献。
扩展:JS中的块级作用域
JS中的函数作用域带来了灵活性,但在一些地方有存在不方便,比如在for、
if语句中,我们可能只需要在它们的
{}里定义一些临时的变量,并不希望污染当前的作用域。基于这些问题,ES6中新增了
let、
const这两个关键字来实现了块级作用域定义变量。
let的用法和
var一样,只不过是作用域不同罢了例如:
const也是用来定义变量,不过它定义的是静态变量(常量),一旦定义了,重新赋值就会报错。
参考文献
JavaScript权威指南
JavaScript高级程序设计
相关文章推荐
- JavaScript基本概念(三)--- 数据类型
- JavaScript实现点击按钮直接打印
- JSP 用JS弹出确认删除对话框
- js中哈希表的几种用法总结
- JS代码大全(都是网上看到自己整理的)
- [html][javascript]父子窗体传值
- javascript中加var和不加var的区别 你真的懂吗
- js,indexOf()、lastIndexOf()
- JavaScript indexOf() 方法
- js 实现图片间隔循环轮播以及没有间隔的循环轮播
- 【RapidJson】Rapidjson解析
- Angualarjs 动画
- JS实现Tab切换
- js传值之乱码解决
- js之闭包
- js中undefined,null和typeof运算符
- js关于setTimeout传参
- js 数组排序 sort()
- js数组操作
- jsreverse和sort排序