在ES6前, 前端就使用RequireJS或者seaJS實(shí)現(xiàn)模塊化, requireJS是基于AMD規(guī)范的模塊化庫, 而像seaJS是基于CMD規(guī)范的模塊化庫, 兩者都是為了為了推廣前端模塊化的工具, 更多有關(guān)AMD和CMD的區(qū)別, 后面參考給了幾個(gè)鏈接;
創(chuàng)新互聯(lián)建站于2013年創(chuàng)立,先為定襄等服務(wù)建站,定襄等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為定襄企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
現(xiàn)在ES6自帶了模塊化, 也是JS第一次支持module, 在很久以后 ,我們可以直接作用import和export在瀏覽器中導(dǎo)入和導(dǎo)出各個(gè)模塊了, 一個(gè)js文件代表一個(gè)js模塊;
現(xiàn)代瀏覽器對模塊(module)支持程度不同, 目前都是使用babelJS, 或者Traceur把ES6代碼轉(zhuǎn)化為兼容ES5版本的js代碼;
ES6的模塊化的基本規(guī)則或特點(diǎn):
ES6的模塊化的基本規(guī)則或特點(diǎn), 歡迎補(bǔ)充:
1:每一個(gè)模塊只加載一次, 每一個(gè)JS只執(zhí)行一次, 如果下次再去加載同目錄下同文件,直接從內(nèi)存中讀取。 一個(gè)模塊就是一個(gè)單例,或者說就是一個(gè)對象;
2:每一個(gè)模塊內(nèi)聲明的變量都是局部變量, 不會污染全局作用域;
3:模塊內(nèi)部的變量或者函數(shù)可以通過export導(dǎo)出;
4:一個(gè)模塊可以導(dǎo)入別的模塊
運(yùn)行下面代碼
//lib.js //導(dǎo)出常量 export const sqrt = Math.sqrt; //導(dǎo)出函數(shù) export function square(x) { return x * x; } //導(dǎo)出函數(shù) export function diag(x, y) { return sqrt(square(x) + square(y)); } //main.js import { square, diag } from './lib'; console.log(square(11)); // 121 console.log(diag(4, 3)); // 5
下面列出幾種import和export的基本語法:
第一種導(dǎo)出的方式:
在lib.js文件中, 使用 export{接口} 導(dǎo)出接口, 大括號中的接口名字為上面定義的變量, import和export是對應(yīng)的;
運(yùn)行下面代碼
//lib.js 文件 let bar = "stringBar"; let foo = "stringFoo"; let fn0 = function() { console.log("fn0"); }; let fn1 = function() { console.log("fn1"); }; export{ bar , foo, fn0, fn1} //main.js文件 import {bar,foo, fn0, fn1} from "./lib"; console.log(bar+"_"+foo); fn0(); fn1();
第二種導(dǎo)出的方式:
在export接口的時(shí)候, 我們可以使用 XX as YY, 把導(dǎo)出的接口名字改了, 比如: closureFn as sayingFn, 把這些接口名字改成不看文檔就知道干什么的:
運(yùn)行下面代碼
//lib.js文件 let fn0 = function() { console.log("fn0"); }; let obj0 = {} export { fn0 as foo, obj0 as bar}; //main.js文件 import {foo, bar} from "./lib"; foo(); console.log(bar);
第三種導(dǎo)出的方式:
這種方式是直接在export的地方定義導(dǎo)出的函數(shù),或者變量:
運(yùn)行下面代碼
//lib.js文件 export let foo = ()=> {console.log("fnFoo") ;return "foo"},bar = "stringBar"; //main.js文件 import {foo, bar} from "./lib"; console.log(foo()); console.log(bar);
第四種導(dǎo)出的方式:
這種導(dǎo)出的方式不需要知道變量的名字, 相當(dāng)于是匿名的, 直接把開發(fā)的接口給export;
如果一個(gè)js模塊文件就只有一個(gè)功能, 那么就可以使用export default導(dǎo)出;
運(yùn)行下面代碼
//lib.js export default "string"; //main.js import defaultString from "./lib"; console.log(defaultString);
第五種導(dǎo)出方式:
export也能默認(rèn)導(dǎo)出函數(shù), 在import的時(shí)候, 名字隨便寫, 因?yàn)槊恳粋€(gè)模塊的默認(rèn)接口就一個(gè):
運(yùn)行下面代碼
//lib.js let fn = () => "string"; export {fn as default}; //main.js import defaultFn from "./lib"; console.log(defaultFn());
第六種導(dǎo)出方式:
使用通配符* ,重新導(dǎo)出其他模塊的接口 (其實(shí)就是轉(zhuǎn)載文章, 然后不注明出處啦);
運(yùn)行下面代碼
//lib.js export * from "./other"; //如果只想導(dǎo)出部分接口, 只要把接口名字列出來 //export {foo,fnFoo} from "./other"; //other.js export let foo = "stringFoo", fnFoo = function() {console.log("fnFoo")}; //main.js import {foo, fnFoo} from "./lib"; console.log(foo); console.log(fnFoo());
其他:ES6的import和export提供相當(dāng)多導(dǎo)入以及導(dǎo)出的語法;
在import的時(shí)候可以使用通配符*導(dǎo)入外部的模塊:
運(yùn)行下面代碼
import * as obj from "./lib"; console.log(obj);
ES6導(dǎo)入的模塊都是屬于引用:
每一個(gè)導(dǎo)入的js模塊都是活的, 每一次訪問該模塊的變量或者函數(shù)都是最新的, 這個(gè)是原生ES6模塊 與AMD和CMD的區(qū)別之一,以下代碼修改自http://exploringjs.com/es6/ch_modules.html#_imports-are-read-only-views-on-exports
運(yùn)行下面代碼
//lib.js export let counter = 3; export function incCounter() { counter++; } export function setCounter(value) { counter = value; } //main.js import { counter, incCounter ,setCounter} from './lib'; // The imported value `counter` is live console.log(counter); // 3 incCounter(); console.log(counter); // 4 setCounter(0); console.log(counter); // 0
在main.js中, counter一直指向lib.js中的局部變量counter, 按照J(rèn)S的尿性, 像數(shù)字或者字符串類型或者布爾值的原始值要被復(fù)制, 而不是賦址;
循環(huán)依賴的問題:
NodeJS的循環(huán)依賴是這么處理的:打開;
循環(huán)依賴是JS模塊化帶來的問題, 在瀏覽器端, 使用RequireJS測試模塊化, 比如有一個(gè)文件file0.js依賴于file1.js, 而file1.js又依賴于file0.js, 那么file0.js和file1.js到底誰先執(zhí)行?
運(yùn)行下面代碼
//index.html
在控制臺的依次輸出為:
運(yùn)行下面代碼
undefined
Object { file1: "file1" }
Object { file0: "file0" }
在執(zhí)行file1.js的時(shí)候file0.js還沒執(zhí)行完, 所以輸出了undefined, 這種輸出結(jié)果和NodeJS輸出的情況是一樣的;
然后我又使用了司徒大神的mass-framework框架試了一下, 司徒大神的框架直接提示我: "模塊與之前的某些模塊存在循環(huán)依賴", 這樣還比較好點(diǎn), requireJS對于循環(huán)依賴是直接執(zhí)行循環(huán)依賴的模塊, 會導(dǎo)致在開發(fā)的時(shí)候給自己挖坑....;
接下來我又在babel-node下進(jìn)行測試:下面是幾個(gè)測試,可以無視:
我使用ES6的模塊試一試, 只要每一個(gè)模塊被引用, 無論模塊是否執(zhí)行完畢, 該模塊的export已經(jīng)被導(dǎo)出了, 如果導(dǎo)出的是函數(shù):
運(yùn)行下面代碼
//cyclic.js import fn0 from "./file0"; fn0(); //file0.js import fn1 from "./file1"; fn1(); console.log("file0.js runs"); export default function() {console.log("file0 export runs")} //file1.js import fn0 from "./file0"; fn0(); console.log("file1.js runs"); export default function() {console.log("file1 export runs")}
如果導(dǎo)出的是字符串:
運(yùn)行下面代碼
//cyclic.js import str from "./file0"; console.log(str); //file0.js import str1 from "./file1"; console.log(str1) console.log("file0.js runs"); export default "str0"; //file1.js import str0 from "./file0"; console.log(str0) console.log("file1.js runs"); export default "str1";
如果導(dǎo)出的是對象:
那么第一行會先輸出一個(gè)初始值{},在最后等待file0.js和file1.js執(zhí)行完畢以后, 才輸出file0.js導(dǎo)出的對象;
如果是數(shù)組:
那么第一行會輸出一個(gè)被靜態(tài)分析過的初始值undefined,在最后等待file0.js和file1.js執(zhí)行完畢以后, 才輸出file0.js導(dǎo)出的對象;
如果是布爾值:
那么第一行會輸出一個(gè)被靜態(tài)分析過的初始值undefined,在最后等待file0.js和file1.js執(zhí)行完畢以后, 才輸出file0.js導(dǎo)出的布爾值;
為什么會這樣呢? 我好像在這邊找到了答案:http://exploringjs.com/es6/ch_modules.html#_modules ,ES6的import和export被提前到j(luò)s的最頂層, 在函數(shù)或者對象,或者基本值被導(dǎo)出去的時(shí)候提前被靜態(tài)分析過,參考:http://www.ecma-international.org/ecma-262/6.0/#sec-parsemodule , http://www.ecma-international.org/ecma-262/6.0/#sec-toplevelmoduleevaluationjob
結(jié)論:用ES6的export導(dǎo)出數(shù)據(jù)接口的時(shí)候, 最好統(tǒng)一用函數(shù), 避免在循環(huán)依賴的時(shí)候, 因?yàn)镴S會把不同類型的對象靜態(tài)解析成不同的初始值;
瀏覽器兼容:
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。