什么是编译时加载与运行时加载

1,812 阅读4分钟

前言 阮一峰老师es6 中Modul篇的记录中有以下这段话:

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。

// CommonJS模块
let { stat, exists, readfile } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

// ES6模块
import { stat, exists, readFile } from 'fs';

上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。

由于 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,比如引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。

这段话我没有理解,上网搜索后也没有相关答案。找了一些资源后将个人理解记录一下。 什么是编译时、运行时 编译时 编译:将源代码翻译成机器能识别的语言(二进制)。计算机还是那个计算机,他至今还是只能看懂01,但随着时间的推移和人类的进步,逐渐出现了很多高级语言,高级语言之所以高级就在于我们可以用一种简单的方式比如if elese 来实现我们的代码逻辑,但最终在执行的时候计算机还是没办法识别的,这个时候就需要一个处理过程"编译",将大家平时写的if else 翻译成机器可以看懂的语言。那负责编译的这部分,我们一般称之为 编译器。

那编译时就会做一些简单的翻译工作,比如检查你有没有粗心写错啥关键字了啊。会进行词法分析,语法分析之类的。就像个老师检查学生的作文中有没有错别字和病句一样.如果发现啥错误编译器就告诉你。开始编译时,如果有errors或者warning信息,都是编译器检查出来的。所谓这时的错误就叫编译时错误,这个过程中做的类型检查也就叫编译时类型检查,或静态类型检查(所谓静态嘛就是没把真把代码放内存中运行起来,而只是把代码当作文本来扫描下)。

运行时
所谓运行时就是代码跑起来了。被装载到内存中去了。(你的代码保存在磁盘上没装入内存之前是个文件。只有跑到内存中才变成活的)。而运行时类型检查就与前面讲的编译时类型检查(或者静态类型检查)不一样。不是简单的扫描代码.而是在内存中做些操作,做些判断以确定我们的程序是否存在错误。

解释型语言和编译型语言
编译型语言:程序在执行之前需要一个专门的编译过程,把程序编译成为机器语言的文件(即exe文件),运行时不需要重新编译,直接用编译后的文件(exe文件)就行了。 优点:执行效率高 缺点:跨平台性差 解释型语言:程序不需要编译,程序在运行的过程中才用解释器编译成机器语言,边编译边执行(没有exe文件)。 优点:跨平台性好、每个平台采用自有的编译机制 缺点:执行效率低、因为边编译边执行 其中程序无需编译,不是说真的不需要编译了,直接执行脚本字符串。而是说不需要在运行之前先编译程序成为exe文件,而是在运行的过程中边运行边执行。 那么我们平时都说js是解释型语言,个人理解是因为他没有生成这种所谓的可执行文件,但其实它的执行中还是掺杂了。

image.png 我的理解 commonjs或AMD,都是运行时加载,在编译阶段只判断语法层面对不对,如果引入了不存在的变量等只有在执行时才会发现。 ES6 可以因为是一边编译一边执行,在编译阶段会检查引入变量是否存在、引入模块是否存在等,问题更早暴露。