详谈commonjs模块与es6模块的区别


Posted in Javascript onOctober 18, 2017

到目前为止,已经实习了3个月的时间了。最近在面试,在面试题里面有题目涉及到模块循环加载的知识。趁着这个机会,将commonjs模块与es6模块之间一些重要的的区别做个总结。语法上有什么区别就不具体说了,主要谈谈引用的区别。

commonjs

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

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

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

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

循环加载时,属于加载时执行。即脚本代码在require的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。

ES6模块

es6模块中的值属于【动态只读引用】。

对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。

对于动态来说,原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。

循环加载时,

上面说了一些重要区别。现在举一些例子来说明每一点吧

commonjs

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

// b.js
let count = 1
let plusCount = () => {
 count++
}
setTimeout(() => {
 console.log('b.js-1', count)
}, 1000)
module.exports = {
 count,
 plusCount
}

// a.js
let mod = require('./b.js')
console.log('a.js-1', mod.count)
mod.plusCount()
console.log('a.js-2', mod.count)
setTimeout(() => {
 mod.count = 3
 console.log('a.js-3', mod.count)
}, 2000)

node a.js
a.js-1 1
a.js-2 1
b.js-1 2 // 1秒后
a.js-3 3 // 2秒后

以上代码可以看出,b模块export的count变量,是一个复制行为。在plusCount方法调用之后,a模块中的count不受影响。同时,可以在b模块中更改a模块中的值。如果希望能够同步代码,可以export出去一个getter。

// 其他代码相同
module.exports = {
 get count () {
 return count
 },
 plusCount
}

node a.js
a.js-1 1
a.js-2 1
b.js-1 2 // 1秒后
a.js-3 2 // 2秒后, 由于没有定义setter,因此无法对值进行设置。所以还是返回2

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

// b.js
let obj = {
 count: 1
}
let plusCount = () => {
 obj.count++
}
setTimeout(() => {
 console.log('b.js-1', obj.count)
}, 1000)
setTimeout(() => {
 console.log('b.js-2', obj.count)
}, 3000)
module.exports = {
 obj,
 plusCount
}

// a.js
var mod = require('./b.js')
console.log('a.js-1', mod.obj.count)
mod.plusCount()
console.log('a.js-2', mod.obj.count)
setTimeout(() => {
 mod.obj.count = 3
 console.log('a.js-3', mod.obj.count)
}, 2000)

node a.js
a.js-1 1
a.js-2 2
b.js-1 2
a.js-3 3
b.js-2 3

以上代码可以看出,对于对象来说属于浅拷贝。当执行a模块时,首先打印obj.count的值为1,然后通过plusCount方法,再次打印时为2。接着在a模块修改count的值为3,此时在b模块的值也为3。

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

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

5.循环加载时,属于加载时执行。即脚本代码在require的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。

3, 4, 5可以使用同一个例子说明

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

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

// c.js
let a = require('./a.js')
let b = require('./b.js')

console.log('c.js-1', '执行完毕', a.done, b.done)

node c.js
b.js-1 false
b.js-2 执行完毕
a.js-1 true
a.js-2 执行完毕
c.js-1 执行完毕 true true

仔细说明一下整个过程。

在Node.js中执行c模块。此时遇到require关键字,执行a.js中所有代码。

在a模块中exports之后,通过require引入了b模块,执行b模块的代码。

在b模块中exports之后,又require引入了a模块,此时执行a模块的代码。

a模块只执行exports.done = false这条语句。

回到b模块,打印b.js-1, exports, b.js-2。b模块执行完毕。

回到a模块,接着打印a.js-1, exports, b.js-2。a模块执行完毕

回到c模块,接着执行require,需要引入b模块。由于在a模块中已经引入过了,所以直接就可以输出值了。

结束。

从以上结果和分析过程可以看出,当遇到require命令时,会执行对应的模块代码。当循环引用时,有可能只输出某模块代码的一部分。当引用同一个模块时,不会再次加载,而是获取缓存。

ES6模块

es6模块中的值属于【动态只读引用】。只说明一下复杂数据类型。

对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。

对于动态来说,原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。

// b.js
export let counter = {
 count: 1
}
setTimeout(() => {
 console.log('b.js-1', counter.count)
}, 1000)

// a.js
import { counter } from './b.js'
counter = {}
console.log('a.js-1', counter)

// Syntax Error: "counter" is read-only

虽然不能将counter重新赋值一个新的对象,但是可以给对象添加属性和方法。此时不会报错。这种行为类型与关键字const的用法。

// a.js
import { counter } from './b.js'
counter.count++
console.log(counter)

// 2

循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就能够执行。

// b.js
import {foo} from './a.js';
export function bar() {
 console.log('bar');
 if (Math.random() > 0.5) {
 foo();
 }
}

// a.js
import {bar} from './b.js';
export function foo() {
 console.log('foo');
 bar();
 console.log('执行完毕');
}
foo();

node a.js
foo
bar
执行完毕

// 执行结果也有可能是
foo
bar
foo
bar
执行完毕
执行完毕

由于在两个模块之间都存在引用。因此能够正常执行。

以上这篇详谈commonjs模块与es6模块的区别就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery中event对象属性与方法小结
Dec 18 Javascript
thinkphp 表名 大小写 窍门
Feb 01 Javascript
javascript每日必学之条件分支
Feb 17 Javascript
玩转JavaScript OOP - 类的实现详解
Jun 08 Javascript
JavaScript仿flash遮罩动画效果
Jun 15 Javascript
微信小程序中实现一对多发消息详解及实例代码
Feb 14 Javascript
vue router学习之动态路由和嵌套路由详解
Sep 21 Javascript
使用vue-route 的 beforeEach 实现导航守卫(路由跳转前验证登录)功能
Mar 22 Javascript
Vue实现按钮旋转和移动位置的实例代码
Aug 09 Javascript
JavaScript中的回调函数实例讲解
Jan 27 Javascript
Vue中 axios delete请求参数操作
Aug 25 Javascript
react如何快速设置文件路径别名
Apr 28 Javascript
从源码看angular/material2 中 dialog模块的实现方法
Oct 18 #Javascript
详解http访问解析流程原理
Oct 18 #Javascript
js实现会跳动的日历效果(完整实例)
Oct 18 #Javascript
打字效果动画的4种实现方法(超简单)
Oct 18 #Javascript
Angularjs 手写日历的实现代码(不用插件)
Oct 18 #Javascript
基于JavaScript表单脚本(详解)
Oct 18 #Javascript
VUE饿了么树形控件添加增删改功能的示例代码
Oct 17 #Javascript
You might like
PHP手机号码归属地查询代码(API接口/mysql)
2012/09/04 PHP
php实现httpclient类示例
2014/04/08 PHP
php中ob函数缓冲机制深入理解
2015/08/03 PHP
详解no input file specified 三种解决方法
2019/11/29 PHP
用JQuery 实现的自定义对话框
2007/03/24 Javascript
基于jquery库的tab新形式使用
2012/11/16 Javascript
jquery div拖动效果示例代码
2013/12/08 Javascript
jQuery中children()方法用法实例
2015/01/07 Javascript
jquery实现select选择框内容左右移动代码分享
2015/11/21 Javascript
Zero Clipboard实现浏览器复制到剪贴板的方法(多个复制按钮)
2016/03/24 Javascript
关于JS 预解释的相关理解
2016/06/28 Javascript
json格式的javascript对象用法分析
2016/07/04 Javascript
vue2.0 中#$emit,$on的使用详解
2017/06/07 Javascript
vue组件实现弹出框点击显示隐藏效果
2020/10/26 Javascript
浅谈在vue中使用mint-ui swipe遇到的问题
2018/09/27 Javascript
基于Node.js搭建hexo博客过程详解
2019/06/25 Javascript
在vue中使用防抖和节流,防止重复点击或重复上拉加载实例
2019/11/13 Javascript
JavaScript 防盗链的原理以及破解方法
2020/12/29 Javascript
[03:08]迎霜节狂欢!2018年迎霜节珍藏Ⅰ一览
2018/12/25 DOTA
[41:52]DOTA2-DPC中国联赛 正赛 CDEC vs Dynasty BO3 第二场 2月22日
2021/03/11 DOTA
python文件比较示例分享
2014/01/10 Python
Python实现的简单dns查询功能示例
2017/05/24 Python
用PyInstaller把Python代码打包成单个独立的exe可执行文件
2018/05/26 Python
使用Python自动化破解自定义字体混淆信息的方法实例
2019/02/13 Python
python PIL/cv2/base64相互转换实例
2020/01/09 Python
Python3变量与基本数据类型用法实例分析
2020/02/14 Python
英国团购网站:Groupon英国
2017/11/28 全球购物
廉价航班、机票和酒店:JustFly
2018/02/07 全球购物
澳大利亚拥有最好的家具和家居用品在线目的地:Nestz
2019/02/23 全球购物
英国空调、除湿机和通风设备排名第一:Air Con Centre
2019/02/25 全球购物
物流毕业生个人的自我评价
2014/02/13 职场文书
趣味比赛活动方案
2014/02/15 职场文书
法学求职信
2014/06/22 职场文书
借名购房协议书范本
2014/10/06 职场文书
庆六一宣传标语
2014/10/08 职场文书
使用compose函数优化代码提高可读性及扩展性
2022/06/16 Javascript