详谈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 相关文章推荐
网页里控制图片大小的相关代码
Jun 13 Javascript
javascript SpiderMonkey中的函数序列化如何进行
Dec 05 Javascript
基于jQuery实现多标签页切换的效果(web前端开发)
Jul 24 Javascript
详细谈谈AngularJS的子级作用域问题
Sep 05 Javascript
Node.js检测端口(port)是否被占用的简单示例
Sep 29 Javascript
基于jQuery实现Tabs选项卡自定义插件
Nov 21 Javascript
JS获取子、父、兄节点方法小结
Aug 14 Javascript
jquery+ajax实现上传图片并显示上传进度功能【附php后台接收】
Jun 06 jQuery
小程序如何自主实现拦截器的示例代码
Nov 04 Javascript
微信小程序语音同步智能识别的实现案例代码解析
May 29 Javascript
彻底搞懂并解决vue-cli4中图片显示的问题实现
Aug 31 Javascript
javascript使用canvas实现饼状图效果
Sep 08 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+APACHE实现用户论证的方法
2006/10/09 PHP
PHP新手上路(十一)
2006/10/09 PHP
php上传apk后自动提取apk包信息的使用(示例下载)
2013/04/26 PHP
PHP基于反射机制实现插件的可插拔设计详解
2016/11/10 PHP
PHP实现的简单操作SQLite数据库类与用法示例
2017/06/19 PHP
详解将数据从Laravel传送到vue的四种方式
2019/10/16 PHP
laravel model模型处理之修改查询或修改字段时的类型格式案例
2019/10/17 PHP
jquery特效 幻灯片效果示例代码
2013/07/16 Javascript
jQuery插件Tooltipster实现漂亮的工具提示
2015/04/12 Javascript
jquery插件之文字间歇自动向上滚动效果代码
2016/02/25 Javascript
详解JavaScript异步编程中jQuery的promise对象的作用
2016/05/03 Javascript
巧用canvas
2017/01/21 Javascript
vue2的todolist入门小项目的详细解析
2017/05/11 Javascript
AngularJS表单验证功能分析
2017/05/26 Javascript
Angular 4依赖注入学习教程之Injectable装饰器(六)
2017/06/04 Javascript
Angular4 中常用的指令入门总结
2017/06/12 Javascript
在微信小程序中渲染HTML内容的方法示例
2018/09/28 Javascript
vue如何获取自定义元素属性参数值的方法
2019/05/14 Javascript
javascript实现点击按钮切换轮播图功能
2020/09/23 Javascript
Django的数据模型访问多对多键值的方法
2015/07/21 Python
Python基于pyCUDA实现GPU加速并行计算功能入门教程
2018/06/19 Python
python进行TCP端口扫描的实现
2018/12/21 Python
学生信息管理系统Python面向对象版
2019/01/30 Python
一文轻松掌握python语言命名规范规则
2020/06/18 Python
python定义类的简单用法
2020/07/24 Python
html5-websocket基于远程方法调用的数据交互实现
2012/12/04 HTML / CSS
护士实习生自我鉴定范文
2013/12/10 职场文书
安全检查验收制度
2014/01/12 职场文书
应届毕业生应聘自荐信范文
2014/02/26 职场文书
总结表彰大会主持词
2014/03/26 职场文书
计划生育证明书写要求
2014/09/17 职场文书
颐和园导游词400字
2015/01/30 职场文书
教师个人学习总结
2015/02/11 职场文书
聚众斗殴罪辩护词
2015/05/21 职场文书
低端且暴利的线上线下创业项目分享
2019/09/03 职场文书
pycharm debug 断点调试心得分享
2021/04/16 Python