编译时与运行时
关于“编译时”与“运行时”的一些个人理解
编译时
所谓编译,就是编译器对代码的一种转化:将源代码转化成机器码,或者一些中间态的代码,比如java代码编译成只有JVM识别的字节码
编译是一个静态过程,所谓静态就是代码并没有放到内存中去运行,只是单纯的一种文本操作。编译过程中做的类型检查工作也叫做静态类型检查
运行时
运行时就是代码被装载到内存中运行起来的阶段,也就是我们最终目的达成的最后阶段(如运行js代码的最终阶段就是运行js代码对应的二进制机器码)
javascript中的编译时与运行时
对于常见编译型语言(例如java)来说,编译步骤分为:词法分析->语法分析->语义检查->中间代码优化->字节(最终代码)生成。
对于解释型语言(例如JavaScript)来说,通过词法分析和语法分析得到语法树后,就可以开始解释执行了。没有语义分析等后面的操作很好理解,也就是因为解释型语言是一句一句执行的,自然不存在一个全局的四元式集合以及针对代码总体的一些优化。(编译原理课程上学的语义分析的输出是四元式集合...这里暂且帮助理解)
对于v8引擎(chrome浏览器)处理js代码的具体执行细节有待学习,但从js代码执行的表现来看,浅显的感受一下js的编译时与运行时:
var的声明提前 && function的声明定义提前:
<script type="text/javascript">
alert(a); // undefined
var a;
</script>
<script type="text/javascript">
func(); // 2
func = function() {alert(1);};
func(); // 1
function func() {alert(2);};
func(); // 1
</script>
在代码执行之前对代码进行如上的操作(变量声明提升),就是js的编译阶段(预编译)所做的一些事情。
题外话:然而JavaScript的“预编译”并不是发生在整个页面的所有脚本中,有人提到是按<script></script>标签“分段编译”的。下面的代码印证了这应该是对的:
<script type="text/javascript">
func(); //2
func = function() {alert(1);};
func(); //1
function func() {alert(2);};
func(); //1
</script>
<script type="text/javascript">
function func() {alert(2);}; // 两个script之间没有互相影响,否则下面的func会输出1,也就是说两个script标签内的js代码如果放到同一个script中,下面的func会输出1(js允许重复声明函数,后声明的覆盖前面的同名函数且与参数无关)
func(); //2
</script>
上面的声明提升等称之为js的“预编译”
- 编译:也就是确实还是在代码运行之前执行的操作,所以也是编译过程的一部分
- 预:操作本身还是在js源代码(高级语言)层面的,也就是说在预编译之后,才会到js代码转机器码的过程,也就是所谓的编译
Vue是一个运行时➕编译时框架
js的运行时与编译时是针对js代码与更底层的二进制机器码之间的概念——js代码编译成二进制,从而运行二进制;
Vue的运行时与编译时是针对用户输入与输出真实dom之间的概念——用户输入<template>模版语法的字符串输出最终的真实dom这一整个过程分成了两部分,第一部分编译时,即模版语法的字符串转化为虚拟dom;第二部分为运行时,即将虚拟dom转化为真实dom。
Vue提供按需使用运行时或者编译时:
- 编译时:提供
compile函数将<template>模版语法的html字符串转化为虚拟dom
- 运行时:提供
render函数将虚拟dom对象转化为真实dom
vue项目运行的一般逻辑也就是项目启动过程中就对所有输入进行compile,并且执行一次render,即初次渲染/挂载,后续用户的操作导致输入有了更新之后,重新进行render,也就是更新渲染/打补丁。
Vue为什么要设计成运行时➕编译时框架/运行时➕编译时框架的优势?
- 对于纯运行时:
虚拟dom=>真实dom,因为不存在编译器,我们必须提供一个复杂的js对象,不利于使用 - 对于纯编译时:
html模版=>真实dom,因为缺少运行时,所以我们只能把diff操作放到编译时进行。相比编译➕运行模式省略了运行时,理论上速度可能会快一些,但是这样就牺牲了灵活性(分的步骤越多自然越灵活)。 - 对于编译时➕运行时:
html模版=>虚拟dom=>真实dom,在运行时(虚拟dom=>真实dom)中加入diff操作,也就是保证灵活性的基础上尽可能的进行优化性能。
总结
所以我认为编译与运行可以理解为一个目标实现过程的前后两个相邻的阶段,而不局限目标的层次(底层或者上层) ,即目标可以是运行js代码,也可以是给Vue框架一个输入然后得到我们想要的页面。