CommonJS
关键词
- 社区标准
- 使用函数实现
- 仅node环境支持
- 动态依赖(需要代码运行后才能确定依赖)
- 动态依赖是同步执行的
原理
// require函数的伪代码
function require(){
if(该模块有缓存吗){
return 缓存结果;
}
function _run(exports,require,module,__filename,_dirname){
// 模块代码会放到这里
}
var module = {
export:{}
}
_run.call({
module.exports,// 全局环境中this的值=module.exports={}
module.exports,// 这五个是run的参数
require,
module,模块路径,
模块所在目录
}):
// 把module.exports加入到缓存;
return module.exports;
}
this === exports === module.exports === {}
在js中打印一下看看值
console.log(arguments.length);
// 5
console.log(exports);
// {}
console.log(require);
/*
[Function: require] {
resolve: [Function: resolve] { paths: [Function: paths] },
main: Module {
id: '.',
path: 'D:\\code\\level1\\工程化\\CommonJS',
exports: {},
parent: null,
filename: 'D:\\code\\level1\\工程化\\CommonJS\\yuanli.js',
loaded: false,
children: [],
paths: [
'D:\\code\\level1\\工程化\\CommonJS\\node_modules',
'D:\\code\\level1\\工程化\\node_modules',
'D:\\code\\level1\\node_modules',
'D:\\code\\node_modules',
'D:\\code\\node_modules',
'D:\\node_modules'
]
},
extensions: [Object: null prototype] {
'.js': [Function (anonymous)],
'.json': [Function (anonymous)],
'.node': [Function (anonymous)]
},
cache: [Object: null prototype] {
'D:\\code\\level1\\工程化\\CommonJS\\yuanli.js': Module {
id: '.',
path: 'D:\\code\\level1\\工程化\\CommonJS',
exports: {},
parent: null,
filename: 'D:\\code\\level1\\工程化\\CommonJS\\yuanli.js',
loaded: false,
children: [],
paths: [Array]
}
}
}
*/
console.log(module);
/*
Module {
id: '.',
path: 'D:\\code\\level1\\工程化\\CommonJS',
exports: {},
parent: null,
filename: 'D:\\code\\level1\\工程化\\CommonJS\\yuanli.js',
loaded: false,
children: [],
paths: [
'D:\\code\\level1\\工程化\\CommonJS\\node_modules',
'D:\\code\\level1\\工程化\\node_modules',
'D:\\code\\level1\\node_modules',
'D:\\code\\node_modules',
'D:\\code\\node_modules',
'D:\\node_modules'
]
}
*/
console.log(__filename);
// D:\code\duyi\level1\工程化\CommonJS\yuanli.js
console.log(__dirname);
// D:\code\duyi\level1\工程化\CommonJS
ES Module
关键词
- 官方标准
- 使用新语法实现,import export等
- 所有环境都支持
- 同时支持静态依赖和动态依赖
- 静态依赖:在代码运行前就要确定依赖关系,必须放在文件顶部。
- 动态依赖是异步的,哪里需要就在那里require。
- 符号绑定
关于静态依赖
关于符号绑定
// a.js
export var a = 1;
export function changeA(){
a = 2;
}
// index.js
import {a as num,changeA} from './a'
console.log(num);
changeA(); // 修改的应该是a.js中的a,理论上不应该影响num
console.log(num);
// 看代码会输出 0 0
// 结果输出的却是:0 1
说明:a和num用的是同一块内存空间。
面试题
- CMJ 和 ESM 区别
- CMJ是社区标准,ESM是官方标准
- CMJ使用的是API实现的模块化,ESM是使用的新语法实现的模块化
- CMJ仅在node环境中支持,ESM在各种环境都支持
- CMJ是动态的依赖,同步执行。ESM既支持动态,也支持静态。动态依赖是异步执行的。
- ESM导出时有符号绑定,CMJ只是普通函数调用和赋值
- export 和 export default的区别是什么?
- export 为普通导出,又叫做具名导出。它导出的数据必须带有命名,比如变量定义、函数定义这种带有命名的语句。在导出的模块对象中,命名即为模块对象的属性名。在一个模板中可以有多个具名导出。
- export default 为默认导出,在模块对象中名称固定为 default,因此无需命名,通常导出一个表达式或字面量。在一个模块中只能有一个默认导出。
- 下面的模块导出了什么结果?
// this === exports === module.exports === {}
exports.a = 'a'; // {a:'a'}
module.exports.b = 'b'; // {a:'a',b:'b'}
this.c = 'c'; // {a:'a',b:'b',c:'c'}
module.exports = { // 这里给exports对象重新赋值了 {d:'d'}
d:'d'
}
{ d: 'd' }
- 下面代码导出了什么?
exports.a = 1;
module.exports.b = {
b:2
}
{ a: 1,b: {b: 2}}
- 下面代码导出什么结果?
// counter.js
var count = 1;
eaport {count}
export function increase(){
count++;
}
// 导出: {count:1,increase:fn}
// main.js
import {count,increase} from './counter';// 同一块内存
import * as counter from './counter';// 同一块内存
const {count:c} = counter;// 单独开辟一块
increase();
console.log(count); // 2
console.log(counter.count);// 2
console.log(c); // 1