提高代码覆盖率,方便进行管理,就有了模块化的规范,目前有 CommonJS、AMD、CMD、ES Module。着重分析一下CommonJS、ES Module
CommonJS (cjs)
使用方法
// 定义模块 c.jsfunction c () {console.log('buer')}module.exports = {c}// 使用模块var d = require('./c.js')d.c()
环境
- CommonJS模块输出的是一个值的复制,ES6模块输出的是值的引用
- CommonJS模块是运行时加载,ES6模块是编译时输出接口
第二个差异是因为CommonJS加载的是一个对象,即module.export属性,该对象只有在脚本运行结束时才会生成。而ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。CommonJS模块输出的是值的复制,一旦输出这个值,模块内部的变化就影响不到这个值。
例如
//lib.js 一个commonJS模块var counter = 3function incCounter() {counter++}module.exports = {counter : counter,incCounter : incCounter,}//main.js 在这个函数里加载这个模块var mod = require ('./lib')console.log(mod.counter)mod.incCounter()console.log(mod.counter)// 打印出来都为 3
ES Module
使用方法
// 定义模块 c.jsexport let c = function () {console.log('buer')}// 使用模块import { c } from './c.jsc()
环境
- 浏览器 (script 标签中需要有属性 type=module
- Node.js (需要node版本>13.2 并且模块名为 .mjs 或者 package.json 中 type=module
特点
- 浏览器中 script标签 加上 type=module后,会跨域(实际上是JS模块的安全性需要
Unlike classic scripts, module scripts require the use of the CORS protocol for cross-origin fetching. — MDN
参考 模块化的背景
- 对比上面 CommonJS,ES6导出的为模块引用,JS引擎对脚本静态分析的时候,遇到模块加载命令import就会生成一个只读引用。等到脚本真正执行的时候,再根据这个只读引用到被加载的模块中取值。因此,ES6模块是动态引用,并且不会缓存值,模块里的变量绑定其所在的模块。 ```javascript //m1.js export var foo = ‘bar’ setTimeout(()=>foo=’baz’,500) //m2.js import {foo} from ‘./m1.js’ console.log(foo) setTimeout(()=>console.log(foo),500)
bar
baz
```
上面的代码表明,ES6模块不会缓存运行结果,而是动态地去被加载的模块取值,并且变量总是绑定其所在的模块。
由于ES6输入的模块变量只是一个“符号连接”,所以这个变量是只读的,对它重新赋值会报错。也就是多次加载都为统一实例
require.js (AMD规范
很少用到,但是AMD思想值得一提
特点
依赖前置,提前执行(也就是直接加载模块然后执行
sea.js (CMD规范
很少用到,但是CMD思想值得一提
特点
依赖就近,延迟执行(也就是直接加载模块然后执行
