学了JS并且用过Node.js后,对模块化应该是有所理解和应用了,那么肯定见过以下两种模块导入导出的形式

第一种: ES6 Module

// B.jsfunction show() {    console.log('show办法被执行')}export default show// A.jsimport show from './B.js'show()  // show办法被执行

第二种: CommonJS

// B.jsfunction show() {    console.log('show办法被执行')}module.exports = {    show}// A.jsconst bModule = require('./B.js')bModule.show()  // show办法被执行

以上两种导入导出办法波及到了两种模块标准,别离是ES6 ModuleCommonJS

本文就来聊聊这两者之间的具体应用与区别

1. CommonJS

CommonJS是2009年由JavaScript社区提出的蕴含了模块化的一个规范,起初被Node.js所采纳并实现,也就是说咱们在Node.js中用到的模块导入导出都是按照CommonJS规范来实现的

1.1 导出

咱们能够把一个文件看成一个模块,每个模块之间是相互独立的,即不会相互影响。当须要应用到某个模块时,只需在文件中将指标模块导入即可

要想被其它模块导入首先须要导出须要向外裸露的变量或办法,在CommonJS中导出的语法有以下两种形式

// B.js// 定义了函数showfunction show() {    console.log('show办法被调用')}// 定义了变量countlet count = 3/*--------------  导出办法  --------------*/// 第一种module.exports = {    show,    count}// 第二种exports.show = showexports.count = count

上述代码中,两种导出形式是等价的。

第一种导出形式是将须要导出的函数或变量存储到 module.exports 外面,其中 module.exports 本来是一个空对象

第二种导出形式中,exports 在外部其实是指向了 module.exports,所以当咱们执行 exports.变量exports.函数 时,其实就相当于把变量或函数存储到 module.exports

留神: 这里要特别强调的是,在应用第二种导出形式时,不能对 exports 进行从新赋值,否则就将 module.exports 间接全副笼罩了

1.2 导入

再来看一下CommonJS的导入语法

// A.jsconst bModule = require('./B.js')console.log(bModule.count)  // 3bModule.show()  // show办法被调用

从上述代码中能够看到,CommonJS是通过 require 办法来导入模块的,其参数为模块文件门路,要特地留神的是,咱们导入模块后接管到的其实是一个对象,也就是 module.exports 的值,咱们能从该对象中获取到所需的变量或函数

另外,比拟特地的是,require 办法还能够接管一个表达式作为参数,代码如下

let fileName = 'B.js'const bModule = require('./' + fileName)

因而,咱们是能够动静的扭转并决定模块的加载导入门路的

2. ES6 Module

如题目名写的,该模块规范是在ES6时才被提出的,尔后JS才具备了模块化这一个性

2.1 导出

在ES6 Module 中,导出用到了关键字 export ,导出的形式也大抵分为两种,别离是命名导出默认导出

第一种: 命名导出

// B.js/*--------  单个变量或函数导出  ----------*/export function show() { console.log('show办法被调用') }export let count = 3/*--------  批量导出  ----------*/function show() { console.log('show办法被调用') }let count = 3export {show, count}

上述代码分了两种状况,且这两种写法是等价的

第一种是单个的变量或函数导出,只须要间接在结尾应用 export 关键字即可;

第二种状况是批量地把多个变量或函数导出,只须要把它们贮存到一个对象中即可

第二种: 默认导出

// B.jsfunction show() { console.log('show办法被调用') }// 命名导出变量countexport let count = 3// 默认导出函数showexport default show

默认导出是在 export 关键词前面再跟上一个 default 示意导出的该变量或函数是匿名的

留神: 一个模块只能默认导出一次,否则就会报错,具体起因会在前面解说

2.2 导入

ES6 Module 的导入用到的关键字是 import ,具体代码如下

// A.jsimport {show, count} from './B.js'show()   // show办法被调用console.log(count)  // 3

ES6 Module的导入须要用一对 {} 大括号来接管咱们须要导入的办法或函数

留神: 大括号中的变量或函数名必须与导出时的名称截然不同

那么如果咱们想批改导入的变量或函数的名称,能够通过 as 关键词来命名,代码如下

// A.jsimport {show as print, count as number} from './B.js'print()   // show办法被调用console.log(number)  // 3

如果咱们要想将所有的变量或函数都导入,能够通过 * 来整体导入,代码如下

import * as bModule from './B.js'bModule.show()  // show办法被调用console.log(bModule.count)  // 3

* 示意全副的意思,咱们将其全副导入,并赋值给 bModule,这样咱们就能够通过 bModule 获取想要的变量或对象了

以上所说的都是针对命名导出的变量或函数,那么如何导入一个默认导出的变量或函数呢?

// 将通过 export default 导出的变量导入import print from './B.js'print()  // show办法被调用

命名导出的变量都是通过 {} 来接管的,那么去掉 {} ,接管的就是默认导出的变量了,因为导出的变量是匿名的,因而咱们能够随便地起个变量名用于接管

补充: 这里特地提一下,与CommonJS不同,ES6 Module 的导入文件门路是不反对表达式的

3. CommonJS 与 ES6 Module 的区别

这两者的次要区别次要有以下两点:

  1. 对于模块的依赖,CommonJS是动静的,ES6 Module 是动态的
  2. CommonJS导入的是值的拷贝,ES6 Module导入的是值的援用

3.1 区别一

对于模块的依赖,何为动静?何为动态

动静是指对于模块的依赖关系建设在代码执行阶段;
动态是指对于模块的依赖关系建设在代码编译阶段;

上文提到,CommonJS导入时,require 的门路参数是反对表达式的,例如

// A.jslet fileName = 'example.js'const bModule = require('./' + fileName)

因为该门路在代码执行时是能够动静扭转的,所以如果在代码编译阶段就建设各个模块的依赖关系,那么肯定是不精确的,只有在代码运行了当前,才能够真正确认模块的依赖关系,因而说CommonJS是动静的。

那么当初你也应该也晓得为什么 ES6 Module 是动态的了吧

3.2 区别二

为了验证这一点,我筹备用实例来演示一下

首先来验证CommonJS,代码如下

// B.jslet count = 3function change() {    count ++    // 变量count + 1    console.log('原count值为:', count);  // 打印B.js模块中count的值}module.exports = {    count,    change}// A.jslet count = require('./B.js').count let change = require('./B.js').changeconsole.log('扭转前:', count);   change()     // 调用模块B.js中的change办法,将原来的count + 1console.log('扭转后:', count); // 运行A.js文件的后果扭转前:3原count值为:4扭转后:3

在上述代码中咱们能够看到,在 A.js 文件中导入了 B.js 文件中的变量 count 和 函数 change,因为导入的 count 只是对原有值的一个拷贝,因而只管咱们调用了函数 change 扭转了 B.js 文件中变量 count 的值,也不会影响到 A.js 文件中的变量 count

依据这个后果得出结论:CommonJS导入的变量是对原值的拷贝


接下来再来验证一下ES6 Module,代码如下

// B.jslet count = 3function change() {    count ++        // 变量count + 1    console.log(count);   // 打印B.js模块中count的值}export {count, change}// A.jsimport {count, change} from './B.js';console.log('扭转前:',count);change()         // 调用模块B.js中的change办法,将原来的count + 1console.log('扭转后:', count);// 运行A.js文件的后果扭转前:3原count值为:4扭转后:4

相比拟于CommonJS的后果,ES6 Module导入的变量 count 随着原值的扭转而扭转了

依据这个后果得出结论:ES6 Module导入的变量是对原值的援用

4. The end

本篇文章就到此结束啦,对于CommonJS和ES6 Module,还有一些本文未提及的常识,还是心愿大家深刻理解

关注公众号:前端印象,每日分享一个高质量前端技术文,还能支付丰盛前端材料