// common/index.js
// 初始化SelectMixin
let selectUrl = '/example/common/getSelectBykeys'
let setSelectUrlPrefix = () => {}
const SelectMixin = select(selectUrl, setSelectUrlPrefix)
// 初始化AuthProviderMixin
let authUrl = '/example/common/authUrl'
let transferAuthResult = () => {}
const AuthProviderMixin = authProvider(authUrl, transferAuthResult)
// 初始化request
let handleRequestError = () => {}
const RequestUtil = request(handleRequestError)
export default {
SelectMixin,
AuthProviderMixin,
RequestUtil
}
複製代碼
import { SelectMixin } from '../common/index.js'
複製代碼
"export 'SelectMixin' was not found in '../common/index.js' 複製代碼
改爲javascript
import SelectMixin from '../common/index.js'
複製代碼
export default {
SelectMixin,
AuthProviderMixin,
RequestUtil
}
複製代碼
module.exports.default = {
SelectMixin,
AuthProviderMixin,
RequestUtil
}
複製代碼
import 語句中的"解構賦值"並非解構賦值,而是 named imports,語法上和解構賦值很像,但仍是有所差異html
上面的問題能夠想着先,而後咱們來看看下面這幾個知識點以後再回來講這個問題。 上面的代碼用了 export default ,而對應的import的在這個時候被稱做 default import。他們是成對使用的。因此用了 export default 則必定要用到 default import。這裏須要記住的知識點有如下幾點。java
// this is default export
// A.js
export default 42
複製代碼
// this is default import
// B.js
import A from './A'
複製代碼
一個模塊只能有一個default exportsnode
default imports 只對 default export 有用, default exports 須要用 default imports 去獲取。webpack
在 default imports 中,導出的時候,能夠爲其任意命名,由於 default export 是匿名的。在下面的例子裏,A, MyA, Something 都是內容相同的。只是其名字不一樣而已,這個語法就是獲取./A值的同時,爲其匿名的對象取個名字git
import A from './A'
import MyA from './A'
import Something from './A'
複製代碼
上面的問題能夠想着先。而後咱們來看看下面這幾個知識點以後再回來講這個問題。上面的代碼用了 named default ,而對應的import的在這個時候被稱做 named import。他們是成對使用的。因此用了 named default 則必定要用到 named import。這裏須要記住的知識點有如下幾點。es6
// this is named export
// A.js
export const A = 42
複製代碼
// this is named import
// B.js
import { A } from './A'
複製代碼
// B.js
import { A } from './A'
import { myA } from './A' // Doesn't work! import { Something } from './A' // Doesn't work!
複製代碼
// A.js
export const A = 42
export const myA = 43
export const Something = 44
複製代碼
// B.js
import { A, myA, Something } from './A'
複製代碼
// B.js
import anyThing, { myA as myX, Something as XSomething } from './A'
複製代碼
咱們能夠把 Default export 當成一個特殊的 named export ,其實default export 也能夠像這樣來解析。只是他默認是叫default的一個對象。切默承認以有任意一個名字去覆蓋他的匿名。github
import { default as anything } from './A'
複製代碼
不過這樣寫也是不被容許的,畢竟default 是一個保留字段。系統會直接報錯,不過講道理,拋去這個保留字段問題,這個寫法按道理也能獲取到default exportweb
import { default } from './A'
複製代碼
在瞭解named import 過程當中碰到一個有意思的點就是 模塊點循環引用。文獻裏說到es6 對循環引用支持比CommonJs更好。特對這個進行了一番瞭解。segmentfault
"循環引用"(circular dependency)指的是,a腳本的執行依賴b腳本,而b腳本的執行又依賴a腳本。
一般,」循環引用"表示存在強耦合,若是處理很差,還可能致使遞歸加載,使得程序沒法執行,所以應該避免出現。
可是實際上,這是很難避免的,尤爲是依賴關係複雜的大項目,很容易出現a依賴b,b依賴c,c又依賴a這樣的狀況。這意味着,模塊加載機制必須考慮」循環引用」的狀況。即便在設計初期經過很好的結構設計避免了,可是代碼一重構,」循環引用」仍是很容易就出現的。因此」循環引用」的狀況是不可能避免的。
這裏不討論其餘靜態文件只考慮腳本文件,CommonJS的一個模塊,就是一個腳本文件。require命令第一次加載該腳本,就會執行整個腳本,而後在內存生成一個對象。以下:
{
id: '',
exports: '',
parent: '',
filename: null,
loader: false,
children: [],
paths: ''
// ...
}
複製代碼
這個對象裏,id屬性是模塊名,exports屬性是模塊輸出的各個接口,loaded屬性是一個布爾值,表示該模塊的腳本是否執行完畢。其餘還有不少屬性,省略。 具體能夠去看 www.ruanyifeng.com/blog/2015/1… 也能夠參考node源碼 github.com/nodejs/node…
當代碼用到這個模塊的時候,就會到exports屬性上面取值。即便再次執行require命令,也不會再次執行該模塊,而是到緩存之中取值。
不說答案,咱們直接說這個在運行c.js時當過程。上邊代碼之中,c.js先引入了a.js。則按照required的原理,會執行a.js整個腳本。
a.js腳本先輸出一個done變量,而後加載另外一個腳本文件b.js。注意,此時a.js代碼就停在這裏,等待b.js執行完畢,再往下執行。 再看b.js的代碼
b.js執行到第二行,就會去加載a.js,這時,就發生了」循環引用」。(CommonJs的循環引用的重要原則:一旦出現某個模塊被」循環引用」,就只輸出已經執行的部分,還未執行的部分不會輸出。) 這裏有得小夥伴會認爲是去執行a.js以前沒執行完的代碼。可是規則不是這麼定義的。由於a.js觸發了循環引用,則a.js會返回已經執行的部分代碼。
系統會去a.js模塊對應對象的exports屬性取值,a.js雖然尚未執行完,可是其exports裏確實有值的,從exports屬性取回已經執行的部分的值,而不是最後的值。 a.js已經執行的部分,只有一行,即 exports.done = false;
因此對於b.js來講,引入的a.js值爲false。而後,b.js接着往下執行,等到所有執行完畢,再把執行權交還給a.js。因而,a.js接着往下執行,直到執行完畢。
c.js就第一句就將a.js和b.js所有加載完畢了。a.js和b.js最終返回的都是true。
因此最終會選擇答案B。
執行a.js 以後的結果是右邊兩種狀況。這個比前面一個例子好理解。咱們先來看一下es6 modules 的加載原理。
ES6模塊的運行機制與CommonJS不同,它遇到模塊加載命令import時,不會去執行模塊,而是隻生成一個引用。 等到真的須要用到時,再到模塊裏面去取值。
所以,ES6模塊是動態引用,不存在緩存值的問題,並且模塊裏面的變量,綁定其所在的模塊。
若是按照CommonJS規範,上面的代碼是無法執行的。a先加載b,而後b又加載a,這時a尚未任何執行結果,因此輸出結果爲null,即對於b.js來講,變量foo的值等於null,後面的foo()就會報錯。
在commonJs裏常常會看到exports 和 module.exports 。這個會比較混淆。
CommonJS定義的模塊分爲: 模塊標識(module)、模塊定義(exports) 、模塊引用(require)。
在一個node執行一個文件時,會給這個文件內生成一個 exports和module對象, 而module又有一個exports屬性。他們之間的關係以下圖,都指向一塊{}內存區域。
簡而言之,區分他們之間的區別就是 exports 只是 module.exports的引用,輔助後者添加內容用的。
對於基本數據類型,屬於複製。即會被模塊緩存。同時,在另外一個模塊能夠對該模塊輸出的變量從新賦值。
對於複雜數據類型,屬於淺拷貝。因爲兩個模塊引用的對象指向同一個內存空間,所以對該模塊的值作修改時會影響另外一個模塊。
當使用require命令加載某個模塊時,就會運行整個模塊的代碼。
當使用require命令加載同一個模塊時,不會再執行該模塊,而是取到緩存之中的值。也就是說,CommonJS模塊不管加載多少次,都只會在第一次加載時運行一次,之後再加載,就返回第一次運行的結果,除非手動清除系統緩存。
循環加載時,屬於加載時執行。即腳本代碼在require的時候,就會所有執行。一旦出現某個模塊被"循環加載",就只輸出已經執行的部分,還未執行的部分不會輸出。
ES6模塊中的值屬於【動態只讀引用】。
對於只讀來講,即不容許修改引入變量的值,import的變量是隻讀的,不管是基本數據類型仍是複雜數據類型。當模塊遇到import命令時,就會生成一個只讀引用。等到腳本真正執行時,再根據這個只讀引用,到被加載的那個模塊裏面去取值。
對於動態來講,原始值發生變化,import加載的值也會發生變化。不管是基本數據類型仍是複雜數據類型。
循環加載時,ES6模塊是動態引用。只要兩個模塊之間存在某個引用,代碼就可以執行。
stackoverflow.com/questions/3…
2ality.com/2014/09/es6… exploringjs.com/es6/ch_modu…
www.ruanyifeng.com/blog/2015/1…