详谈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 相关文章推荐
Firefox下提示illegal character并出现乱码的原因
Mar 25 Javascript
一个基于jquery的文本框记数器
Sep 19 Javascript
json对象转字符串如何实现
Dec 02 Javascript
jquery formValidator插件ajax验证 内容不做任何修改再离开提示错误的bug解决方法
Jan 04 Javascript
深入解析contentWindow, contentDocument
Jul 04 Javascript
JavaScript实现维吉尼亚(Vigenere)密码算法实例
Nov 22 Javascript
JS实用的动画弹出层效果实例
May 05 Javascript
JS常用加密编码与算法实例总结
Dec 22 Javascript
详解用webpack2.0构建vue2.0超详细精简版
Apr 05 Javascript
关于AngularJS中几种Providers的区别总结
May 17 Javascript
使用Vue-cli 中为单独页面设置背景图片铺满全屏
Jul 17 Javascript
在Angular项目使用socket.io实现通信的方法
Jan 05 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中数据库单例模式的实现代码分享
2014/08/21 PHP
PHP中的使用curl发送请求(GET请求和POST请求)
2017/02/08 PHP
PHP 的Opcache加速的使用方法
2017/12/29 PHP
PHPCrawl爬虫库实现抓取酷狗歌单的方法示例
2017/12/21 PHP
通过javascript获取iframe里的值示例代码
2013/06/24 Javascript
jquery实现触发时更新下拉列表内容的方法
2015/12/02 Javascript
JavaScript标准对象_动力节点Java学院整理
2017/06/27 Javascript
vue使用中的内存泄漏【推荐】
2018/07/10 Javascript
原生JS实现贪吃蛇小游戏
2020/03/09 Javascript
vue循环中点击选中再点击取消(单选)的实现
2020/09/10 Javascript
JavaScript 防盗链的原理以及破解方法
2020/12/29 Javascript
Python设计模式之代理模式实例
2014/04/26 Python
python实现的udp协议Server和Client代码实例
2014/06/04 Python
神经网络python源码分享
2017/12/15 Python
pycharm运行程序时在Python console窗口中运行的方法
2018/12/03 Python
python中文分词库jieba使用方法详解
2020/02/11 Python
使用python实现下载我们想听的歌曲,速度超快
2020/07/09 Python
python字典key不能是可以是啥类型
2020/08/04 Python
pip已经安装好第三方库但pycharm中import时还是标红的解决方案
2020/10/09 Python
python 基于opencv 绘制图像轮廓
2020/12/11 Python
西班牙鞋子和箱包在线销售网站:zapatos.es
2020/02/17 全球购物
公司营业员的工作总结自我评价
2013/10/05 职场文书
总经理职责范文
2013/11/08 职场文书
服装设计专业毕业生推荐信
2013/11/09 职场文书
电子商务网站的创业计划书
2014/01/05 职场文书
上班迟到检讨书
2014/01/10 职场文书
上班打牌检讨书
2014/02/07 职场文书
学前班评语大全
2014/05/04 职场文书
人事行政主管岗位职责
2015/04/09 职场文书
学校捐书活动总结
2015/05/08 职场文书
2015年社区居委会工作总结
2015/05/18 职场文书
SpringBoot连接MySQL获取数据写后端接口的操作方法
2021/11/02 MySQL
Linux下使用C语言代码搭建一个简单的HTTP服务器
2022/04/13 Servers
MySQL的存储过程和相关函数
2022/04/26 MySQL
MySQL中正则表达式(REGEXP)使用详解
2022/07/07 MySQL
Zabbix6通过ODBC方式监控Oracle 19C的详细过程
2022/09/23 Servers