CommonJS模块与ES6模块的区别(require和import)

Posted shuaigebie

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CommonJS模块与ES6模块的区别(require和import)相关的知识,希望对你有一定的参考价值。

模块化的不同解决方案

追根溯源,JS这门脚本语言设计伊始就是没有模块化的,所以早期的全局变量容易造成命名冲突。但随着web项目越来越大,JS的代码量也与日俱增,于是社区就自发约定了几种模块化的方案:requirejs遵循AMD,seajs遵循CMD,node的module遵循CommonJS规范,虽然写法上有所不同,都是为了能够间接实现模块化的基础上保持较为一致的代码风格。

随着ES2015的发布,官方标准定义了一种模块化的方案,那就是import、export。可是,标准毕竟是标准,各大浏览器和node终端要实现标准还是有一段距离的,目前来说都2018年了主流浏览器都还没实现,还得依赖转换工具(例如babel)转为ES5的代码之后浏览器才能解析。所以这也就解释了为什么我们的工程化代码中nodeJS遵循的CommonJS规范和ES6的模块化方案并存的现象。

CommonJS

1.CommonJS中使用了require导入模块,module.exports导出模块,而module.exports中可以引用exports导出,这两者有什么区别呢?

//a.js
module.exports = count

//b.js
var result = require("./a")
console.log(result)
//node b.js
//输出 0 



//将a.js中改为
module.exports.count =count
//node b.js
//输出 {count:0}

//将a.js中改为
exports.count = count
//node b.js
//输出 {count:0}

//将a.js中改为
exports= count
//node b.js
//输出 {}

 

 

 

上面的代码可以得出结论:module.exports和exports均指向一个空对象,而require导入的模块是module.exports导出的模块(当改变module.exports的指向时,require导入的是count不是{count:0};改变exports指向时,得到{ }对象),

exports只是module.exports中的一个引用(当exports导出时变量,module.exports也会增加对应的变量)

 

2.当使用require命令加载某个模块时,就会运行整个模块的代码

 

//a.js
var count = 0 ;
console.log(‘123‘)

module.exports = count

//b.js
var result = require("./a")
console.log(result)

//node b.js
//输出 123 0

 

 

 3.对于基本数据类型,属于复制。即会被模块缓存。同时,在另一个模块可以对该模块输出的变量重新赋值。

4.对于复杂数据类型,属于浅拷贝。由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块。

// a.js
let count = 0;
var add = function () {
    count++
}
var obj = {
    count:10,
    add(){
        obj.count++
    }
}

module.exports = {add,count,obj};

//b.js
var result = require("./a")
result.add()
console.log(result.count)// 0

result.obj.add()
console.log(result.obj.count)// 11


 上述案例可以看出:当导入基本数据类型时,两个模块没有任何关联(改变a中的值对于b没有影响),而当导入复杂数据类型时,改变a中的值,b中的值也同样被修改了;

 

5.当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。

技术图片

 

 上述代码中,b.js中只被加载了一次

 

ES6模块规范

不同于CommonJS,ES6使用 export 和 import 来导出、导入模块。export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。import采用的是编译时加载,所以import导入的模块必须放在代码的顶部,模块指向也是指向同一个内存对象,所以当改变内存指向的对象的值时,导入值会随之改变(和require导入复杂类型对象相似)

// test.js
var firstName = ‘Michael‘;
var lastName = ‘Jackson‘;
var year = 1958;
export {firstName, lastName, year};
// demo.js
import { firstName } from ‘./test.js‘
console.log(firstName);  // ‘Michael‘
import * as test from ‘./test.js‘
console.log(test);  //Module{}   所有内容
import { firstName as key, lastName as value } from ‘./test.js‘
console.log(key + ‘--‘ + value);  // Michael--Jackson

export default(导出默认值)

var firstName = ‘Michael‘;
var lastName = ‘Jackson‘;
var year = 1958;
export default { firstName, lastName, year };
// demo.js
import test from ‘./test.js‘
console.log(test); // {firstName: "Michael", lastName: "Jackson", year: 1958}

这里相当于

import { default as test } from ‘./test.js‘

 

总结

CommonJS模块规范和ES6模块规范可以说是两种不同的概念,其作用类似,只是规范的不同导致了使用的方法以及性能的不同

 

以上是关于CommonJS模块与ES6模块的区别(require和import)的主要内容,如果未能解决你的问题,请参考以下文章

CommonJS与ES6模块化的具体使用方式

ES6 模块与 CommonJS 模块的差异

CommonJS模块与ES6模块的区别(require和import)

commonjs 与 ES6 模块化

一文彻底弄懂 “CommonJs” 与 “EsModule” 区别

ES6模块和commonjs模块的区别