详谈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 相关文章推荐
20个最新的jQuery插件
Jan 13 Javascript
Javascript中引用示例介绍
Feb 21 Javascript
jQuery实现滑动页面固定顶部显示(可根据显示位置消失与替换)
Oct 28 Javascript
js与jQuery实现checkbox复选框全选/全不选的方法
Jan 05 Javascript
js调用屏幕宽度的简单方法
Nov 14 Javascript
JavaScript基于DOM操作实现简单的数学运算功能示例
Jan 16 Javascript
微信小程序开发之选项卡(窗口底部TabBar)页面切换
Apr 12 Javascript
微信小程序tabBar用法实例详解
Dec 04 Javascript
使用webpack搭建react开发环境的方法
May 15 Javascript
webpack的CSS加载器的使用
Sep 11 Javascript
vue操作动画的记录animate.css实例代码
Apr 26 Javascript
webpack 动态批量加载文件的实现方法
Mar 19 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 分页原理分析,大家可以看看
2009/12/21 PHP
php中echo()和print()、require()和include()等易混淆函数的区别
2012/02/22 PHP
php创建、获取cookie及基础要点分析
2015/01/26 PHP
php计算2个日期的差值函数分享
2015/02/02 PHP
PHP 正则表达式小结
2015/02/12 PHP
php技巧小结【推荐】
2017/01/19 PHP
PHP中$GLOBALS['HTTP_RAW_POST_DATA']和$_POST的区别分析
2017/07/03 PHP
PHP高效获取远程图片尺寸和大小的实现方法
2017/10/20 PHP
php之header的不同用法总结(实例讲解)
2017/11/28 PHP
php-fpm.conf配置文件中文说明详解及重要参数说明
2018/10/10 PHP
总结PHP中初始化空数组的最佳方法
2019/02/13 PHP
php使用mysqli和pdo扩展,测试对比mysql数据库的执行效率完整示例
2019/05/09 PHP
laravel 错误处理,接口错误返回json代码
2019/10/25 PHP
url 特殊字符 传递参数解决方法
2010/01/01 Javascript
基于jquery的lazy loader插件实现图片的延迟加载[简单使用]
2011/05/07 Javascript
Sort()函数的多种用法
2016/03/20 Javascript
你可能不知道的前端算法之文字避让(inMap)
2018/01/12 Javascript
微信小程序实现提交input信息到后台的方法示例
2019/01/19 Javascript
jquery实现点击弹出对话框
2020/02/08 jQuery
Node.js API详解之 vm模块用法实例分析
2020/05/27 Javascript
朴素贝叶斯算法的python实现方法
2014/11/18 Python
Python实现的栈(Stack)
2018/01/26 Python
python实现定时自动备份文件到其他主机的实例代码
2018/02/23 Python
Python的log日志功能及设置方法
2019/07/11 Python
django 利用Q对象与F对象进行查询的实现
2020/05/15 Python
Pyecharts 中Geo函数常用参数的用法说明
2021/02/01 Python
Envie de Fraise意大利:法国网上推出的孕妇装品牌
2020/10/18 全球购物
美术专业个人自我评价
2014/01/18 职场文书
企业演讲比赛主持词
2014/03/18 职场文书
节能环保家庭事迹材料
2014/08/27 职场文书
中学生旷课检讨书2篇
2014/10/09 职场文书
2014年审计人员工作总结
2014/12/19 职场文书
针对吵架老公保证书
2015/05/08 职场文书
军事博物馆观后感
2015/06/05 职场文书
python 破解加密zip文件的密码
2021/04/22 Python
Python万能模板案例之matplotlib绘制甘特图
2022/04/13 Python