详解JS作用域(一)
一、什么是作用域
存储和访问变量,是任何一种编程语言最基本的功能之一,变量存在哪里?程序需要时如何找到它?这些问题需要一套良好的规则来规范,这套规则,就成为作用域。
二、编译原理
js通常归类为解释语言,但它其实是编译语言,和传统编译语言不同,它不是提前编译,编译结果也不能在分布式系统中进行移植。js引擎进行编译的步骤和传统的编译语言非常相似,传统编译语言,程序中的一段源码在执行之前会经历三个步骤,统称为“编译”。
2.1 分词/词法分析
这个过程会讲由字符组成的祖父穿分解成有意义的代码块。这些代码块叫词法单元。例如:var a=2;会分解成以下这些词法单元:
var、a、=、2、;
空格是否会被当做词法单元,取决于空格在这门语言中是否具有意义。
2.2 解析/词法分析
这个过程是将词法单元流转成一个由元素逐级嵌套所组成的代表了程序语法结构的树,这个树叫做抽象语法树(AST)。
var a=2; 的抽象语法数中可能会有一个叫做VariableDeclaration的顶级节点,接下来是一个叫做Identifier(它的值是a)的节点,以及一个叫做AssignmentExpression的子节点,AssignmentExpression节点有一个叫做NumericLiteral(它的值是2)的子节点。
2.3 代码生成
将AST转换成可执行代码的过程叫做代码生成,简单来说就是有某种方法可以讲var a=2 ;的AST转化成一组机器指令,用来创建一个叫做a的变量(包括分配内存等),并将一个值存在a中。
比起那些编译过程只要三个步骤的语言编译器,js引擎要复杂得多,例如,在语法分析和代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。简单来说,任何js代码片段在执行前都要进行编译(通常就在执行前),因此js编译器首先会对var a=2;这段程序进行编译,然后做好执行他的准备,并且通常马上就会执行它。
三、理解作用域
3.1 参与到var a=2;进行处理的过程中的演员们
引擎:从头到尾负责整个js程序的编译和执行过程
编译器:引擎的好朋友,负责词法分析以及代码生成等脏活累活。
作用域:引擎的另外一个朋友,负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
3.2 对话
看看var a=2;的执行过程。
编译器首先会将这段代码分解成词法单元,然后将词法单元解析成一个树结构,但是当编译器进行代码生成时,他对这段程序的处理方式和预期的有所不同。可以合理地假设编译器所产生的代码能够用下面的伪代码进行概括:“为一个变量分配内存,将其命名为a,然后将值2保存在这个变量”,然而,这并不完全正确。
事实上编译器会进行如下处理:
遇到var a,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中,如果是,编译器会忽略该声明,继续进行编译,否则他会要求作用域在当前作用域的集合中声明一个新的变量,命名为a。
接下来编译器会为引擎声称运行时所需的代码,这些代码备用处理a=2这个赋值操作。引擎运行时会首先询问作用域,在当前的作用域集合中是否存在一个叫做a的变量,如果是,引擎会使用这个变量,否则,引擎会继续查找变量。
如果引擎最终找到了a变量,就会将2赋值给它,否则引擎会举手示意抛出一个异常。
所以,变量的复制操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后再运行时引擎会在作用域中查找这个变量,如果能够找到就会对他赋值。
3.3 编译器有话说
编译器在编译过程的第二步中生成了代码,引擎执行它时,会通过查找变量a来判断是否已声明过,查询的过程由作用域协助,但是引擎执行怎么样的查询,会影响最终的查询结果。
引擎为会a进行LHS查询,另外一个查询的类型叫做RHS。
当变量出现在赋值操作的左侧时,进行LHS查询,出现在右侧时进行RHS查询。
console.log(a); //对a的引用是一个RHS引用
a=2;//对a的引用是LHS引用
三、作用域嵌套
当一个块或者函数嵌套在另外一个块或者函数中时,就发生了作用域嵌套,在当前作用域中无法找到这个变量时,引擎就会在外层嵌套的作用域中继续找,直到找到该变量,或抵挡最外层的作用域位置。
function foo(a){
console.log(a+b);
}
var b=2;
foo(2);
引擎:foo作用域兄弟,你见过b吗?我需要对它进行RHS引用。
作用域:没有
引擎:foo的上级作用域兄弟,你见过b没?我需要对它进行RHS引用。
作用域:当然了,给你!
四、总结
作用域是一套规则,用于确定在何处以及如何查找变量,如果查找的目的是对变量赋值,那么就使用LHS查询,如果目的是获取变量的值,就用RHS查询,
详解JS作用域(一)的更多相关文章
- 详解js变量、作用域及内存
详解js变量.作用域及内存 来源:伯乐在线 作者:trigkit4 原文出处: trigkit4 基本类型值有:undefined,NUll,Boolean,Number和Strin ...
- 详解js和jquery里的this关键字
详解js和jquery里的this关键字 js中的this 我们要记住:this永远指向函数运行时所在的对象!而不是函数被创建时所在的对象.this对象是在运行时基于函数的执行环境绑定的,在全局环境中 ...
- [转]javascript console 函数详解 js开发调试的利器
javascript console 函数详解 js开发调试的利器 分步阅读 Console 是用于显示 JS和 DOM 对象信息的单独窗口.并且向 JS 中注入1个 console 对象,使用该 ...
- 详解js的bind、call、apply
详解js的bind.call.apply 说明 虽然bind.call.apply都是js很基础的一块知识,但是我从未认真总结过这三者的区别. 由于公司后端是用的微服务架构,又没有中间层对接,导致前端 ...
- 详解js面向对象编程
转自:http://segmentfault.com/a/1190000000713346 基本概念 ECMA关于对象的定义是:”无序属性的集合,其属性可以包含基本值.对象或者函数.“对象的每个属性或 ...
- 详解js中的闭包
前言 在js中,闭包是一个很重要又相当不容易完全理解的要点,网上关于讲解闭包的文章非常多,但是并不是非常容易读懂,在这里以<javascript高级程序设计>里面的理论为基础.用拆分的方式 ...
- Jquery 选择器 详解 js 判断字符串是否包含另外一个字符串
Jquery 选择器 详解 在线文档地址:http://tool.oschina.net/apidocs/apidoc?api=jquery 各种在线工具地址:http://www.ostools ...
- js详解之作用域-实例
函数如下大家可以做做看 function aa(a,b,c){ function a(){} console.log(a); console.log(aa); console.log(argument ...
- 详解JS设计模式
原文链接:www.cnblogs.com 一:理解工厂模式 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式. 简单的工厂模式可以理解为解决 ...
随机推荐
- uva 401 Palindromes 解题报告
题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...
- github如何提交自己修改的代码
当在github上发现别人项目有BUG,或者想要完善其功能的时候,该如何把自己的修改提交到项目中呢? 以logback为例 步骤: 1, fork一份logback代码到自己的仓库 进入github要 ...
- Linux档案属性
输入命令:ls -al 档案类型权限: 第一個字元代表这个档案是『目录.档案或链接档等等』: 当为[ d ]则是目录: 当为[ - ]则是目录: 若是[ l ]则表示为链接档(link file): ...
- codeforces 701D D. As Fast As Possible(数学)
题目链接: D. As Fast As Possible time limit per test 1 second memory limit per test 256 megabytes input ...
- 数据表示Numpy
1 基本 1.1 基本介绍 掌握表示, 清洗, 统计和展示数据的能力 Numpy, Matplotlib, Pandas, Projects 摘要: 有损的提取数据特征的过程 可以将一组数据, 摘要出 ...
- asterisk用 freetds记录 cdr 到mssql
Compile, configure, and install the latest FreeTDS package: [pre] tar -zxvf freetds-0.62.4 ...
- 51nod1228
伯努利数 这个是答案 其中的b是伯努利数,可以n^2预处理 伯努利数n^2递推 #include<bits/stdc++.h> using namespace std; typedef l ...
- 安装ubuntu+Windows双系统, Windows启动项消失
这里主要介绍grub分区损坏的问题: 首先介绍最简单的方法, 不确定能不能成功, 但是最好先用此方法, 毕竟最简单如果解决就不用下一个方法了. 1. (1)用U盘做一个ubuntu的镜像, 开机进入U ...
- Andriod开发 --插件安装、环境配置、问题集锦
1.用Eclipse搭建Android开发环境和创建第一个Android项目(Windows平台) 链接阅读http://www.cnblogs.com/allenzheng/archive/2012 ...
- httpclient:实现有验证码的模拟登陆
//1.这种方式是先把验证码的图片下载到本地.并且根据网页解析获得token值//2.手动在控制台输入验证码//3.因为验证码图片已经下载下来,后面就可以使用图像文字识别package DoubanS ...