commonJS 和 ES Module 的区别

测试环境

node v13.12.0

先上结论

commonJS ES Module
1. 基本类型:值复制,不共享
2. 引用类型:浅拷贝,共享
3. 工作空间可以修改引入的值
1. 只读导入,动态读取
2. 不可在外部修改引用,但可以调用引用中包含的的方法
执行顺序 1. 检查是否有该模块的缓存
2. 如果有,则使用缓存
3. 如果没有,则执行该模块代码,并缓存
1. 检查该模块是否引入过?
2. 是,暂时认该模块为 {}
3. 否,进入该模块并执行其代码(不做缓存)
⚠️import 会被提升到最先执行
CommonJS模块和ES6模块的区别 - 凯斯keith - 博客园 ES6中循环引用的坑 - 杨光的笔记

1. 对导入值的验证

commonJS

// mod.js
let count = 1;
let friends = ['夏洛'];

function plusCount() {
    count++
};

function plusYuanhua() {
    friends.push('袁华');
}

setInterval(() => {
    console.log('mod.js 每秒打印 - count', count);
    console.log('mod.js 每秒打印 - friends', friends);
}, 1000);

module.exports = {
    count,
    friends,
    plusCount,
    plusYuanhua,
}


// index.js
const mod = require('./mod.js');

console.log('index.js 初次导入 - mod.count', mod.count);
console.log('index.js 初次导入 - mod.friends', mod.friends);

mod.plusCount();
mod.plusYuanhua();

console.log('index.js 执行 mod.plusCount/plusYuanhua 后 - mod.count', mod.count);
console.log('index.js 执行 mod.plusCount/plusYuanhua 后 - mod.friends', mod.friends);

setTimeout(() => {
    mod.count = 3;
    console.log('index.js 延时2s - mod.count', mod.count);
    console.log('index.js 延时2s - mod.friends', mod.friends);
}, 2000)

/*
index.js 初次导入 - mod.count 1
index.js 初次导入 - mod.friends [ '夏洛' ]

index.js 执行 mod.plusCount/plusYuanhua 后 - mod.count 1
index.js 执行 mod.plusCount/plusYuanhua 后 - mod.friends [ '夏洛', '袁华' ]

mod.js 每秒打印 - count 2
mod.js 每秒打印 - friends [ '夏洛', '袁华' ]

mod.js 每秒打印 - count 2
mod.js 每秒打印 - friends [ '夏洛', '袁华' ]

index.js 延时2s - mod.count 3
index.js 延时2s - mod.friends [ '夏洛', '袁华' ]

mod.js 每秒打印 - count 2
mod.js 每秒打印 - friends [ '夏洛', '袁华' ]
*/



ES Module

Node 13.2 增加了对 ES Module 的支持,为使 node 识别 ES Module,你需要做如下之一

  1. 更改 .js.mjs,并使用 node index.mjs
  2. package.json添加字段"type": "module",如此所有 .js 均被识别为 ES Module
// index.mjs
import { counter } from './mod.mjs'
counter = {}; // TypeError: Assignment to constant variable.
console.log('a.js-1', counter)

// mod.mjs
export let counter = {
    count: 1
}
setInterval(() => {
    console.log('modB.js-1', counter.count)
}, 1000)

可见 import 为只读引入,如此,mod.mjs 中 count 变化会及时的反应在 index.mjs 中

2. 模块导入,它们做了什么?

commonJS

// index.js
let a = require('./modA.js')
let b = require('./modB.js')
console.log('index.js-1', '执行完毕', a.done, b.done)

// modA.js
exports.done = false
let b = require('./modB.js')
console.log('modA.js-1', b.done)
exports.done = true
console.log('modB.js-2', '执行完毕')

// modB.js
exports.done = false
let a = require('./modA.js')
console.log('modB.js-1', a.done)
exports.done = true
console.log('modB.js-2', '执行完毕')

/*
modB.js-1 false
modB.js-2 执行完毕
modA.js-1 true
modB.js-2 执行完毕
index.js-1 执行完毕 true true
*/

执行顺序是这样的:

  1. node 执行 index.js 文件,发现 require('./modA.js'),暂停 index.js 代码执行,进入 modA 模块
  2. 在 modA 中发现 require('./modB.js'),暂停 modA 代码执行,将已执行的部分 modA 代码缓存,随后进入 modB 模块
  3. 在 modB 中发现require('./modA.js')提取已缓存的部分 modA (因为 modA 代码全部执行完),执行所有的 modB 代码,完毕后,缓存 modB 执行结果,执行栈返回至 modA
  4. 执行 modA 剩余代码,完毕后,缓存 modA,执行栈返回 index.js
  5. 在 index.js 发现 require('./modBjs')提取已有的 modB 缓存,执行剩余代码

ES Module

// index.mjs
console.log("before import mod")
import { b } from "./mod.mjs"
console.log("b is " + b)
export let a = b + 1;

// mod.mjs
console.log("before import index")
import { b } from "./index.mjs"
console.log("b is " + b)
export let a = b + 1;

/*
before import a
a is undefined
before import b
b is NAN
*/

这里的顺序也很简单,

  1. 进入 index.mjs ,提升 import('./modB.mjs') 语句至顶部,最先执行,进入 modB
  2. 提升 提升 import('./index.mjs') 语句至顶部,发现已经执行过 index.mjs,此处将从 index 导入的内容当作 { },执行剩余代码。完毕后,执行栈返回 index.mjs
  3. 执行剩余代码

你可能感兴趣的:(commonjs,es6)