JavaScript运行机制

前言

JavaScript是一个单线程、解释型语言。

单线程的意思很简单,就是同一个时间只能做一件事。

编译型语言和解释型语言区别:

  • 编译型语言:C、C++、java此类编译型语言,运行过程:给人看的代码文件->机器能运行的代码文件->运行结果,下次执行不需要再重复此过程,这个过程叫做编译。
  • 解释型语言:JavaScript、Python此类解释型语言,自上而下,解释一行,执行一行;不会通篇编译为一个文件再执行。

解释型语言:程序不需要编译,程序在运行时才翻译成机器语言,每执行一次都要翻译一次。

编译型语言:程序在执行之前需要一个专门的编译过程,把程序编译成 为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果。

JavaScript执行过程

  1. 语法分析
  2. 预编译
  3. 解释执行

JavaScript按照代码块来进行解释和执行,代码块间相互独立,但变量和方法共享。

JavaScript中的代码块是指由<script></script>标签分割的代码段。

1. 语法分析

顾名思义 就是检查一遍js代码内有没有出现语法错误(比如少些个分号,多写个括号等);语法分析期间不会执行代码。

2. 预编译

在此之前,有些人可能会有疑问:js不是说好了是解释型语言吗?怎么还有预编译,它到底能编译吗?事实上,JavaScript确实是一门解释型语言,但它有编译特性(取决于引擎)。
可以参考下面两篇文章

https://www.cnblogs.com/allen2333/p/9148230.html
https://baijiahao.baidu.com/s?id=1591528352410621144&wfr=spider&for=pc

预编译发生在什么时候?

脚本代码块script执行前(全局)或者函数执行前(函数体内)

预编译发生在哪些范围内?

全局范围和函数体内。

  • 全局:全局的变量声明和函数声明则会存放在全局对象内(Global Object 简称GO,它是window的一部分,你可以直接把他理解成window对象)中
  • 函数体内
    预编译会提前把函数里的变量声明和函数声明依据规则存放在该活动对象内(Activation Object,简称AO),

预编译简单理解就是在内存中开辟一些空间,存放一些变量与函数 。

预编译大致可分为4步:

  1. 创建AO/GO对象
  2. 找形参和变量声明,将形参和变量名作为AO/GO属性名,值为undefined
  3. 将实参值和形参统一
  4. 在函数体里面找函数声明,值赋予函数体。

预编译小结

函数声明整体提升

  1. 创建AO对象(Active Object)
  2. 查找函数形参及函数内变量声明,形参名及变量名作为AO对象的属性,值为undefined
  3. 实参形参相统一,实参值赋给形参
  4. 查找函数声明,函数名作为AO对象的属性,值为函数引用

变量声明提升

  1. 查找全局变量声明(包括隐式全局变量声明,省略var声明),变量名作全局对象的属性,值为undefined
  2. 查找函数声明,函数名作为全局对象的属性,值为函数引用
    (具体点说,无论变量调用和声明的位置是前是后,系统总会把声明移到调用前,注意仅仅只是声明,所以值是undefined),只有在解释执行阶段才会进行变量初始化,匿名函数不参与预编译。
    预编译前奏

imply global 即任何变量,如果未经声明就赋值,则此变量就为全局变量所有(全局域就是window) 。一切声明的全局变量,全是window的属性。

JavaScript中函数定义主要有两种:声明式与函数表达式。

1
2
3
4
5
6
7
8
//声明式函数
function test() {
//...
}
//函数表达式
var test = function() {
//...
}

代码实例

1.变量声明提升

1
2
3
4
alert(a);
alert('ok');
var a = 1;
/*弹出undefined和ok。因为执行时先解释:定义var变量,并未初始化赋值,当前值为undefined。*/

2.未定义声明即为全局变量

1
2
3
alert(a);
alert('ok');
a = 1;

会发现报错了,因为a未定义,解释时定义var变量,并不会定义此处的a。

到这里我们又发现了一个值得关注的问题–定义变量的方式。

JavaScript变量分两种:全局变量和局部变量。像a = 1;这种定义默认是创建全局变量,其实就相当于window.a = 1;而var a = 1;这种格式是定义一个当前作用域下的变量。解释时只会定义var格式的变量。

3.下面代码做了什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
var a = 1;// 变量声明
function b(y){//函数声明
var x = 1;
console.log('so easy');
};
var c = function(){//是变量声明而不是函数声明!!
//...
}
b(100);
</script>

<script>
var d = 0;
</script>
  • 页面产生便创建了GO全局对象(Global Object)(也就是大家熟悉的window对象)
  • 第一个脚本文件加载
  • 脚本加载完毕后,分析语法是否合法
  • 开始预编译
    • 查找变量声明,作为GO属性,值赋予undefined
    • 查找函数声明,作为GO属性,值赋予函数体

3. 解释执行

预编译完毕之后,JavaScript 脚本开始执行,执行顺序按照从上到下的顺序执行。

总结

总结
JavaScript执行顺序

  1. 语法分析
  2. 预编译
    1. 创建AO(GO)对象
    1. 找形参和变量声明,将形参和变量名作为AO(GO)属性名,值为undefined
    1. 将实参值和形参统一
    1. 在函数体里面找函数声明,值赋予函数体。
  3. 解释执行