yii beforeaction 如何赋值全局变量_JavaScript 是如何执行代码的

本文转载于 SegmentFault 社区

社区专栏:前端

作者:君前


 

准备知识

在讲正文之前,我们需要先了解几个角色和概念: ·  引擎: 引擎爸爸的工作,负责整个 JavaScript 程序的编译及执行过程 ·   编译器: 引擎的好朋友,负责词法、语法分析及代码生成等脏活累活 ·   作用域: 引擎的另一个好朋友,负责创建并维护所有的声明 (变量,函数) ,并实施一套严格的规则,规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
如果想深入学习作用域的相关知识请查看 《JavaScript深入之作用域》
我们经常说 JavaScript 是一门解释型语言,区别于编译型语言,但是实际上 JavaScript 是一门编译语言。但与传统的编译语言不同, 它不是提前编译的, 编译结果也不能在分布式系统中进行移植。 事实上,任何 JavaScript 代码片段在执行前都要进行编译,然后做好执行它的准备,通常编译后就会马上执行。 我们以下面这段代码为例来说明 JavaScript 到底是如何执行代码的。   
var a = 2;
function m() {
console.log('m');
}
m();

JavaScript 是如何执行代码的

上面我们说了 JavaScript 在执行代码前是先进行编译的,上述代码虽然只有三块 (变量声明、函数声明、函数调用) ,但是对于引擎来说却相当于四个指令: ·   var a  和  function m() {} :编译阶段执行; (所有的变量和函数声明都是在编译阶段执行的) ·   a = 2  和  m( ); :执行阶段执行; 下面我们将详细介绍一下在编译和执行阶段具体是如何处理的。

编译阶段

如果不了解编译原理的相关知识(词法分析、语法分析、AST、代码生成),请查看 《编译原理之基础篇》https://segmentfault.com/a/1190000021931476
1. 编译器首先会将这段程序分解成词法单元 (词法分析) ,然后将词法单元解析成 AST (语法分析) ,然后开始根据 AST 生成机器指令 (代码生成) 。 2. 在代码生成阶段:
当编译器遇到  var a  时,编译器会询问作用域是否已经存在一个该名称的标识符,如果存在,编译器则忽略该指令,继续编译;否则它会要求作用域在当前作用域的集合中声明一个命名为 a 的标识符 (变量) 。
同理,在编译器遇到  function m() {}  时,也会去询问作用域是否存在命名为 m 的函数声明,如果存在,编译器则忽略该指令,继续编译;否则它会要求作用域在当前作用域的集合中声明一个命名为 m 的函数。 3. 编译器为引擎生成运行时所需代码之后,引擎开始执行代码。

执行阶段

引擎在执行代码时: 遇到  a = 2  时,会询问作用域,在当前作用域是否存在一个叫作 a 的标识符,如果是,作用域就会将其返还给引擎,引擎则会使用这个标识符;如果否,引擎会继续查找该变量 (询问当前作用域的父级作用域,依次类推,直到顶层(全局作用域)) 。 如果引擎最终找到了 a ,就会将 2 赋值给它。 否则在严格模式下,引擎会抛出一个  ReferenceError 异常 (同作用域判别失败相关,作用域中没有找到想要的标识符); 在非严格模式下,如果在顶层 (全局作用域) 中也无法找到目标标识符,全局作用域中就会隐式创建一个具有该名称的标识符, 并将其返还给引擎。 总结:变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后再运行时引擎会在作用域中查找该变量,如果能够找到就会对它赋值。
   当引擎遇到 m(); 时,查找 m 标识符的过程同上。不同点是:在找不到 m 是,在非严格模式下,不会隐式创建一个具有该名称的标识符,在下面 【执行过程中引擎是如何查找变量的】 中会解释原因。 还有一个需要注意的点是,当找到 m 标识符后,引擎会开始执行该函数。此时如果该标识符代表的是一个函数,那么函数可以正常执行;但是如果标识符代表的是一个变量,那么就会抛出 TypeError 异常  (代表作用域判别成功了, 但是对结果的操作是非法或不合理的) ,如下图所示: 2bc1ad5d0c4c35c0ffcb5ee129c79ee7.png   此时 a 是一个变量,所以当尝试执行它时,会报错 a 不是一个函数。

执行过程中引擎是如何查找变量的

上面我们留了一个疑问,就是为什么在找不到函数标识符的时候,全局作用域不会像变量声明一样也隐式创建一个,下面我们就来看看是为什么。 引擎在执行代码时,会通过查找标识符 a 和 m 来判断它是否已经声明过。查找的过程由作用域进行协助,但是引擎是怎么查找的呢?引擎查找变量有两种方式,分别是: LHS 查询:  
如果查找的目的是对变量进行赋值,则使用 LHS 查询 (告诉作用域我需要对 a 变量进行 LHS 引用,你见过它嘛?)
不成功的 LHS 引用会导致自动隐式创建一个全局变量 (非严格模式) ,严格模式下抛出 ReferenceError 异常。 RHS 查询: 如果查找的目的是获取变量的值,则使用 RHS 查询 (告诉作用域我需要对 a 变量进行 RHS 引用,你见过它嘛?)  
不成功的 RHS 查询会抛出 ReferenceError 异常。  
 

思考题

请看下面这个例子,其中 RHS 共使用了三次,LHS 共使用了两次,你能找到都是在哪里使用了 RHS 和 LHS 吗?
function add(a, b) {
return a + b;
}
add(1, 2)
参考资料:《你不知道的JavaScript》 上篇

  - END - 617db1c864aca19e24519abd430d6725.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值