对于ES6模块与CommonJS模块理解

390 阅读4分钟

ES6模块与CommonJS模块理解

CommonJS理解

CommonJS就是为JS的表现来制定规范,因为js没有模块的功能所以CommonJS应运而生,它希望js可以在任何地方运行,不只是浏览器中。通常使用在nodeJs中。 使用exports module 或 exports 进行模块导出,使用require进行模块的导入

exports 和 exports module 区别

为了方便,Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令

const exports = module.exports

不能将exports和module.exports重新赋值,如果重新赋值不会生效的 如下面这几种情况,

exports = function fn (a) {console.log(a)} // 不能直接将exports变量指向一个值,因为这样等于切断了exports与module.exports的联系 
// module.exports被重新赋值了, exports与module.exports联系也没有了,fn函数不会被导出
exports.fn = function (a) {console.log(a)}
module.exports = 'hello word'

正确做法应该是这样子

// common.js
let fn = function (a) {console.log(a)}
let str = 'hello word'
module.exports.str = str
exports.fn = fn // exports = module.exports 代码中可以只用一种即可

// index.js
const common = require('./common');
console.log(common) // { str: 'hello word', fn: [Function: fn] }

注意事项:package.json中不能设置type为module,否则会报错,原因是设置为module后,需要使用ES6的模块化方案

模块的缓存

第一次加载某个模块时,Node会缓存该模块。以后再加载该模块,就直接从缓存取出该模块的module.exports属性

// common.js
module.exports.a = 1

// index.js
let common = require('./common')
common.b = 2
let common1 = require('./common')
console.log(common1) // { a: 1, b: 2 }

所有缓存的模块保存在require.cache之中,如果想删除模块的缓存,可以像下面这样写。

// 删除指定模块的缓存
delete require.cache[moduleName];

// 删除所有模块的缓存
Object.keys(require.cache).forEach(function(key) {
  delete require.cache[key];
})

总结:

  • 所有代码运行在模块作用域,不会污染全局作用域
  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
  • 模块加载的顺序,按照其在代码中出现的顺序

ES6模块化理解

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量,像CommonJS模块是在运行时加载,相比较ES6模块,就会效率没那么高。

  • ES6模块必须用export导出
  • export 必须与模块内部的变量建立一一对应关系

如何使用ES6模块

// 首先需要建立初始化package.json文件
  npm init -y
// 在package.json中设置type:"module"
  "name": "practice",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module", // 这个
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": ""

export 和 export default的区别

  • export可以多次的出现,export default只能出现一次
export const str = '123'
export let fn = function (a) {console.log(a)}

let b = '456'
let c = '789'
export default b
export default c // error 报错
  • export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系
// 报错
export 1;

// 报错
const m = 1;
export m;

// 正确写法
export cont a = 1
const m = 1
export {m}
  • export default 就是输出一个叫做default的变量或方法,所以后面不能直接跟变量的声明语句
// 错误书写
export deafult const a = 1 // 报错

// 正确书写
let a = 1
export default a // 导入时直接为1
// 或
export default {
	a //导入时为{ a: 1 }
}

// 因为直接就是导出default的变量所以可以直接跟值
expore deafult 1

import的使用

  • import命令输入的变量都是只读的
// util.js
export let a = {}

// index.js
import { a } from './util.js'
a = { foo:1 } // 报错
a.foo = 1
console.log(a) // { foo: 1 }
  • import命令具有提升效果
// util.js
console.log(2)
export function fn () {
	console.log(3)
}

// index.js
console.log(1)
import { fn } from './util.js'
fn()
// 打印顺序 2 1 3
  • import是静态执行,所以不能使用表达式和变量
  • import语句是 Singleton 模式

不同的接收方式

  • 接受export 导出的模块
// util.js
export const a = 1
export let fn = function (a) {console.log(a)}
// 或
const a = 1
let fn = function (a) {console.log(a)}
export { a, fn }

// index.js
// 导入模块名的名字必须与导出的名字相同
import { a, fn } from './util.js' 
// 可以使用 as 来重命名
import { a as b, fn as fn1 } from './util.js'
// 可以使用 * 来把所有都出的模块合成一个对象
import * as data from './utils' // { a: 1, fn: function }

-接受export defalut 导出的模块

// util.js
const a = 1
let fn = function (a) {console.log(a)}
export default {
	a,
	fn
}

// index.js
// 导入时的可以随便取
import util from './util.js'
console.log(util) // { a: 1, fn: function }

-同时接受export 和 export default 导出的模块时用各自的接受方式接受即可,也可以使用* as data 把所有模块都放在data里面

ES6模块与CommonJS模块区别

  • CommonJs模块输出的是一个值的拷贝,只是一个浅拷贝的过程,如果改变的是对象里面的值,还是会改变的。
// common.js
let a = 1
let fn = function () {
	a++
}
module.exports.a = a
module.exports.fn = fn

// index.js
let common = require('./common')
console.log(common.a) // 1
common.fn()
console.log(common.a) // 1
  • ES6模块输出的是值的引用。
// util.js
export let a = 1
export let fu = function () {
	a++
}

// index.js
import { a, fn } from './util.js'
console.log(a) // 1
fn()
console.log(a) // 2
  • CommonJs模块是运行时加载,ES6模块是编译时输出接口。 因为 CommonJS 加载的是一个对象(即module.exports属性)。该对象只有在脚本运行完才会生成。而ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态编译阶段就会生成