詳談commonjs模塊與es6模塊的區(qū)別
到目前為止,已經(jīng)實(shí)習(xí)了3個(gè)月的時(shí)間了。最近在面試,在面試題里面有題目涉及到模塊循環(huán)加載的知識(shí)。趁著這個(gè)機(jī)會(huì),將commonjs模塊與es6模塊之間一些重要的的區(qū)別做個(gè)總結(jié)。語(yǔ)法上有什么區(qū)別就不具體說(shuō)了,主要談?wù)勔玫膮^(qū)別。
commonjs
對(duì)于基本數(shù)據(jù)類型,屬于復(fù)制。即會(huì)被模塊緩存。同時(shí),在另一個(gè)模塊可以對(duì)該模塊輸出的變量重新賦值。
對(duì)于復(fù)雜數(shù)據(jù)類型,屬于淺拷貝。由于兩個(gè)模塊引用的對(duì)象指向同一個(gè)內(nèi)存空間,因此對(duì)該模塊的值做修改時(shí)會(huì)影響另一個(gè)模塊。
當(dāng)使用require命令加載某個(gè)模塊時(shí),就會(huì)運(yùn)行整個(gè)模塊的代碼。
當(dāng)使用require命令加載同一個(gè)模塊時(shí),不會(huì)再執(zhí)行該模塊,而是取到緩存之中的值。也就是說(shuō),commonjs模塊無(wú)論加載多少次,都只會(huì)在第一次加載時(shí)運(yùn)行一次,以后再加載,就返回第一次運(yùn)行的結(jié)果,除非手動(dòng)清除系統(tǒng)緩存。
循環(huán)加載時(shí),屬于加載時(shí)執(zhí)行。即腳本代碼在require的時(shí)候,就會(huì)全部執(zhí)行。一旦出現(xiàn)某個(gè)模塊被"循環(huán)加載",就只輸出已經(jīng)執(zhí)行的部分,還未執(zhí)行的部分不會(huì)輸出。
ES6模塊
es6模塊中的值屬于【動(dòng)態(tài)只讀引用】。
對(duì)于只讀來(lái)說(shuō),即不允許修改引入變量的值,import的變量是只讀的,不論是基本數(shù)據(jù)類型還是復(fù)雜數(shù)據(jù)類型。當(dāng)模塊遇到import命令時(shí),就會(huì)生成一個(gè)只讀引用。等到腳本真正執(zhí)行時(shí),再根據(jù)這個(gè)只讀引用,到被加載的那個(gè)模塊里面去取值。
對(duì)于動(dòng)態(tài)來(lái)說(shuō),原始值發(fā)生變化,import加載的值也會(huì)發(fā)生變化。不論是基本數(shù)據(jù)類型還是復(fù)雜數(shù)據(jù)類型。
循環(huán)加載時(shí),
上面說(shuō)了一些重要區(qū)別?,F(xiàn)在舉一些例子來(lái)說(shuō)明每一點(diǎn)吧
commonjs
對(duì)于基本數(shù)據(jù)類型,屬于復(fù)制。即會(huì)被模塊緩存。同時(shí),在另一個(gè)模塊可以對(duì)該模塊輸出的變量重新賦值。
// 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變量,是一個(gè)復(fù)制行為。在plusCount方法調(diào)用之后,a模塊中的count不受影響。同時(shí),可以在b模塊中更改a模塊中的值。如果希望能夠同步代碼,可以export出去一個(gè)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秒后, 由于沒(méi)有定義setter,因此無(wú)法對(duì)值進(jìn)行設(shè)置。所以還是返回2
對(duì)于復(fù)雜數(shù)據(jù)類型,屬于淺拷貝。由于兩個(gè)模塊引用的對(duì)象指向同一個(gè)內(nèi)存空間,因此對(duì)該模塊的值做修改時(shí)會(huì)影響另一個(gè)模塊。
// 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
以上代碼可以看出,對(duì)于對(duì)象來(lái)說(shuō)屬于淺拷貝。當(dāng)執(zhí)行a模塊時(shí),首先打印obj.count的值為1,然后通過(guò)plusCount方法,再次打印時(shí)為2。接著在a模塊修改count的值為3,此時(shí)在b模塊的值也為3。
3.當(dāng)使用require命令加載某個(gè)模塊時(shí),就會(huì)運(yùn)行整個(gè)模塊的代碼。
4.當(dāng)使用require命令加載同一個(gè)模塊時(shí),不會(huì)再執(zhí)行該模塊,而是取到緩存之中的值。也就是說(shuō),commonjs模塊無(wú)論加載多少次,都只會(huì)在第一次加載時(shí)運(yùn)行一次,以后再加載,就返回第一次運(yùn)行的結(jié)果,除非手動(dòng)清除系統(tǒng)緩存。
5.循環(huán)加載時(shí),屬于加載時(shí)執(zhí)行。即腳本代碼在require的時(shí)候,就會(huì)全部執(zhí)行。一旦出現(xiàn)某個(gè)模塊被"循環(huán)加載",就只輸出已經(jīng)執(zhí)行的部分,還未執(zhí)行的部分不會(huì)輸出。
3, 4, 5可以使用同一個(gè)例子說(shuō)明
// 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', '執(zhí)行完畢')
// 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', '執(zhí)行完畢')
// c.js
let a = require('./a.js')
let b = require('./b.js')
console.log('c.js-1', '執(zhí)行完畢', a.done, b.done)
node c.js
b.js-1 false
b.js-2 執(zhí)行完畢
a.js-1 true
a.js-2 執(zhí)行完畢
c.js-1 執(zhí)行完畢 true true
仔細(xì)說(shuō)明一下整個(gè)過(guò)程。
在Node.js中執(zhí)行c模塊。此時(shí)遇到require關(guān)鍵字,執(zhí)行a.js中所有代碼。
在a模塊中exports之后,通過(guò)require引入了b模塊,執(zhí)行b模塊的代碼。
在b模塊中exports之后,又require引入了a模塊,此時(shí)執(zhí)行a模塊的代碼。
a模塊只執(zhí)行exports.done = false這條語(yǔ)句。
回到b模塊,打印b.js-1, exports, b.js-2。b模塊執(zhí)行完畢。
回到a模塊,接著打印a.js-1, exports, b.js-2。a模塊執(zhí)行完畢
回到c模塊,接著執(zhí)行require,需要引入b模塊。由于在a模塊中已經(jīng)引入過(guò)了,所以直接就可以輸出值了。
結(jié)束。
從以上結(jié)果和分析過(guò)程可以看出,當(dāng)遇到require命令時(shí),會(huì)執(zhí)行對(duì)應(yīng)的模塊代碼。當(dāng)循環(huán)引用時(shí),有可能只輸出某模塊代碼的一部分。當(dāng)引用同一個(gè)模塊時(shí),不會(huì)再次加載,而是獲取緩存。
ES6模塊
es6模塊中的值屬于【動(dòng)態(tài)只讀引用】。只說(shuō)明一下復(fù)雜數(shù)據(jù)類型。
對(duì)于只讀來(lái)說(shuō),即不允許修改引入變量的值,import的變量是只讀的,不論是基本數(shù)據(jù)類型還是復(fù)雜數(shù)據(jù)類型。當(dāng)模塊遇到import命令時(shí),就會(huì)生成一個(gè)只讀引用。等到腳本真正執(zhí)行時(shí),再根據(jù)這個(gè)只讀引用,到被加載的那個(gè)模塊里面去取值。
對(duì)于動(dòng)態(tài)來(lái)說(shuō),原始值發(fā)生變化,import加載的值也會(huì)發(fā)生變化。不論是基本數(shù)據(jù)類型還是復(fù)雜數(shù)據(jù)類型。
// 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重新賦值一個(gè)新的對(duì)象,但是可以給對(duì)象添加屬性和方法。此時(shí)不會(huì)報(bào)錯(cuò)。這種行為類型與關(guān)鍵字const的用法。
// a.js
import { counter } from './b.js'
counter.count++
console.log(counter)
// 2
循環(huán)加載時(shí),ES6模塊是動(dòng)態(tài)引用。只要兩個(gè)模塊之間存在某個(gè)引用,代碼就能夠執(zhí)行。
// 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('執(zhí)行完畢');
}
foo();
node a.js
foo
bar
執(zhí)行完畢
// 執(zhí)行結(jié)果也有可能是
foo
bar
foo
bar
執(zhí)行完畢
執(zhí)行完畢
由于在兩個(gè)模塊之間都存在引用。因此能夠正常執(zhí)行。
以上這篇詳談commonjs模塊與es6模塊的區(qū)別就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Javascript實(shí)現(xiàn)關(guān)聯(lián)數(shù)據(jù)(Linked Data)查詢及注意細(xì)節(jié)
DBpedia對(duì)Wikipedia的數(shù)據(jù)變成Linked Data形式,使得機(jī)器也能讀懂并自由獲得這些數(shù)據(jù);本文的主要目的是利用Javascript從DBpedia中獲取我們想要的數(shù)據(jù),感興趣的朋友可以參考下,希望可以幫助到你2013-02-02
JavaScript中檢測(cè)數(shù)組的3種方法小結(jié)
數(shù)組檢測(cè)是指在編程中對(duì)數(shù)組進(jìn)行驗(yàn)證和檢查的過(guò)程,本文主要介紹了JavaScript中檢測(cè)數(shù)組的3種方法小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08
詳解JavaScript 中g(shù)etElementsByName在IE中的注意事項(xiàng)
這篇文章主要介紹了詳解JavaScript 中g(shù)etElementsByName在IE中的注意事項(xiàng)的相關(guān)資料,需要的朋友可以參考下2017-02-02
Js實(shí)現(xiàn)中國(guó)公民身份證號(hào)碼有效性驗(yàn)證實(shí)例代碼
這篇文章主要介紹了Js實(shí)現(xiàn)中國(guó)公民身份證號(hào)碼有效性驗(yàn)證實(shí)例代碼,可以識(shí)別身份證號(hào)碼的正確性,有興趣的可以了解一下2017-05-05
javascript文件加載管理簡(jiǎn)單實(shí)現(xiàn)方法
這篇文章主要介紹了javascript文件加載管理簡(jiǎn)單實(shí)現(xiàn)方法,可實(shí)現(xiàn)順序加載所有js文件的功能,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2015-07-07
js中symbol類型以及symbol的三大應(yīng)用場(chǎng)景詳解
Symbol是ES6新推出的一種基本類型,它表示獨(dú)一無(wú)二的值,它可以接受一個(gè)字符串作為參數(shù),帶有相同參數(shù)的兩個(gè)Symbol值不相等,這個(gè)參數(shù)只是表示Symbol值的描述而已,下面這篇文章主要給大家介紹了關(guān)于js中symbol類型以及symbol的三大應(yīng)用場(chǎng)景,需要的朋友可以參考下2022-09-09

