├── Module 的语法
│ ├── 概述
│ │ └─ let { stat, exists, readfile } = require('fs');
│ │ └─ 上面代码的实质是整体加载`fs`模块(即加载`fs`的所有方法),生成一个对象(`_fs`),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。
│ │ └─ import { stat, exists, readFile } from 'fs';
│ │ └─ 上面代码的实质是从`fs`模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。
│ │ └─ ES6 模块还有以下好处。
│ │ └─ - 不再需要`UMD`模块格式了,将来服务器和浏览器都会支持 ES6 模块格式。目前,通过各种工具库,其实已经做到了这一点。
│ │ └─ - 将来浏览器的新 API 就能用模块格式提供,不再必须做成全局变量或者`navigator`对象的属性。
│ │ └─ - 不再需要对象作为命名空间(比如`Math`对象),未来这些功能可以通过模块提供。
│ ├── 严格模式
│ │ └─ - 变量必须声明后再使用
│ │ └─ - 函数的参数不能有同名属性,否则报错
│ │ └─ - 不能使用`with`语句
│ │ └─ - 不能对只读属性赋值,否则报错
│ │ └─ - 不能使用前缀 0 表示八进制数,否则报错
│ │ └─ - 不能删除不可删除的属性,否则报错
│ │ └─ - 不能删除变量`delete prop`,会报错,只能删除属性`delete global[prop]`
│ │ └─ - `eval`不会在它的外层作用域引入变量
│ │ └─ - `eval`和`arguments`不能被重新赋值
│ │ └─ - `arguments`不会自动反映函数参数的变化
│ │ └─ - 不能使用`arguments.callee`
│ │ └─ - 不能使用`arguments.caller`
│ │ └─ - 禁止`this`指向全局对象
│ │ └─ - 不能使用`fn.caller`和`fn.arguments`获取函数调用的堆栈
│ │ └─ - 增加了保留字(比如`protected`、`static`和`interface`)
│ │ └─ 需要注意`this`的限制。ES6 模块之中,顶层的`this`指向`undefined`,即不应该在顶层代码使用`this`。
│ ├── export 命令
│ │ └─ 模块功能主要由两个命令构成:`export`和`import`。`export`命令用于规定模块的对外接口,`import`命令用于输入其他模块提供的功能。
│ │ └─ `export`输出的变量就是本来的名字,但是可以使用`as`关键字重命名。
│ │ └─ `export`语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。
│ │ └─ `export`命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错,`import`命令也是如此。这是因为处于条件代码块之中,就没法做静态优化了,违背了 ES6 模块的设计初衷。
│ ├── import 命令
│ │ └─ `import`命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。
│ │ └─ `import`命令要使用`as`关键字,将输入的变量重命名。
│ │ └─ `import`后面的`from`指定模块文件的位置,可以是相对路径,也可以是绝对路径。如果不带有路径,只是一个模块名,那么必须有配置文件,告诉 JavaScript 引擎该模块的位置。
│ │ └─ `import`命令具有提升效果,会提升到整个模块的头部,首先执行。
│ │ └─ 由于`import`是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
│ │ └─ 多次重复执行同一句`import`语句,那么只会执行一次,而不会执行多次。