es6模塊化

 在ES6前, 前端就使用RequireJS或者seaJS實現模塊化, requireJS是基於AMD規範的模塊化庫, 而像seaJS是基於CMD規範的模塊化庫, 二者都是爲了爲了推廣前端模塊化的工具, 更多有關AMD和CMD的區別, 後面參考給了幾個連接;css

 

   如今ES6自帶了模塊化, 也是JS第一次支持module, 在好久之後 ,咱們能夠直接做用import和export在瀏覽器中導入和導出各個模塊了, 一個js文件表明一個js模塊;html

 

  現代瀏覽器對模塊(module)支持程度不一樣, 目前都是使用babelJS, 或者Traceur把ES6代碼轉化爲兼容ES5版本的js代碼;前端

 

 

  ES6的模塊化的基本規則或特色:node

 

  ES6的模塊化的基本規則或特色, 歡迎補充:es6

 

    1:每個模塊只加載一次, 每個JS只執行一次, 若是下次再去加載同目錄下同文件,直接從內存中讀取。 一個模塊就是一個單例,或者說就是一個對象;chrome

 

    2:每個模塊內聲明的變量都是局部變量, 不會污染全局做用域;數組

 

    3:模塊內部的變量或者函數能夠經過export導出;瀏覽器

 

    4:一個模塊能夠導入別的模塊babel

 

運行下面代碼框架

 

 

//lib.js

//導出常量

export const sqrt = Math.sqrt;

//導出函數

export function square(x) {

    return x * x;

}

//導出函數

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的基本語法:

 

  第一種導出的方式:

 

  在lib.js文件中, 使用 export{接口} 導出接口, 大括號中的接口名字爲上面定義的變量, import和export是對應的;

 

運行下面代碼

 

 

//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();

 

    

 

  第二種導出的方式:

 

   在export接口的時候, 咱們能夠使用 XX as YY, 把導出的接口名字改了, 好比: closureFn as sayingFn, 把這些接口名字改爲不看文檔就知道幹什麼的:

 

運行下面代碼

 

 

//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);

 

 

 

    

 

  第三種導出的方式:

 

  這種方式是直接在export的地方定義導出的函數,或者變量:

 

運行下面代碼

 

 

//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);

 

 

 

    

 

  第四種導出的方式:

 

  這種導出的方式不須要知道變量的名字, 至關因而匿名的, 直接把開發的接口給export;

  若是一個js模塊文件就只有一個功能, 那麼就能夠使用export default導出;

 

運行下面代碼

 

 

//lib.js

export default "string";

 

//main.js

import defaultString from "./lib";

console.log(defaultString);

 

 

 

    

 

  第五種導出方式:

 

  export也能默認導出函數, 在import的時候, 名字隨便寫, 由於每個模塊的默認接口就一個:

 

運行下面代碼

 

 

//lib.js

let fn = () => "string";

export {fn as default};

 

//main.js

import defaultFn from "./lib";

console.log(defaultFn());

 

 

 

  第六種導出方式:

 

  使用通配符* ,從新導出其餘模塊的接口 (其實就是轉載文章, 而後不註明出處啦);

 

運行下面代碼

 

 

//lib.js

export * from "./other";

//若是隻想導出部分接口, 只要把接口名字列出來

//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提供至關多導入以及導出的語法;

 

  在import的時候能夠使用通配符*導入外部的模塊:

 

運行下面代碼

 

import * as obj from "./lib";

console.log(obj);

回到頂部

  ES6導入的模塊都是屬於引用:

 

  每個導入的js模塊都是活的, 每一次訪問該模塊的變量或者函數都是最新的, 這個是原生ES6模塊 與AMD和CMD的區別之一,如下代碼修改自http://exploringjs.com/es6/ch_modules.html#_imports-are-read-only-views-on-exports

 

運行下面代碼

 

 

//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, 按照JS的尿性, 像數字或者字符串類型或者布爾值的原始值要被複制, 而不是賦址;

 

 

  循環依賴的問題:

 

 

  NodeJS的循環依賴是這麼處理的:打開;

 

  循環依賴是JS模塊化帶來的問題, 在瀏覽器端, 使用RequireJS測試模塊化, 好比有一個文件file0.js依賴於file1.js, 而file1.js又依賴於file0.js, 那麼file0.js和file1.js到底誰先執行?

 

運行下面代碼

 

 

//index.html

<!DOCTYPE html>

<html>

<head>

    <title></title>

    <meta charset="utf-8"/>

</head>

<body>

 

<script data-main="cyclic" src="//cdn.bootcss.com/require.js/2.2.0/require.min.js"></script>

<script>

//cyclic.js

require(["file0"], function(file0) {

    console.log(file0)

})

 

//file0.js

define(["file1"], function(file1) {

    console.log(file1)

    return {

        file0 : "file0"

    }

})

 

//file1.js

define(["file0"], function(file0) {

    console.log(file0);

    return {

        file1 : "file1"

    }

})

</script>

</body>

</html>

 

 

 

  在控制檯的依次輸出爲:

 

運行下面代碼

 

undefined

Object { file1: "file1" } 

Object { file0: "file0" }

  在執行file1.js的時候file0.js還沒執行完, 因此輸出了undefined, 這種輸出結果和NodeJS輸出的狀況是同樣的;

 

  而後我又使用了司徒大神的mass-framework框架試了一下, 司徒大神的框架直接提示我: "模塊與以前的某些模塊存在循環依賴", 這樣還比較好點, requireJS對於循環依賴是直接執行循環依賴的模塊, 會致使在開發的時候給本身挖坑....;

 

  接下來我又在babel-node下進行測試:下面是幾個測試,能夠無視:

 

  我使用ES6的模塊試一試, 只要每個模塊被引用, 不管模塊是否執行完畢, 該模塊的export已經被導出了, 若是導出的是函數:

 

運行下面代碼

 

 

//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")}

 

 

 

  

 

  若是導出的是字符串:

 

運行下面代碼

 

 

//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";

 

 

 

  

 

  若是導出的是對象:

 

  那麼第一行會先輸出一個初始值{},在最後等待file0.js和file1.js執行完畢之後, 才輸出file0.js導出的對象;

 

  若是是數組:

 

  那麼第一行會輸出一個被靜態分析過的初始值undefined,在最後等待file0.js和file1.js執行完畢之後, 才輸出file0.js導出的對象;

 

  若是是布爾值:

 

  那麼第一行會輸出一個被靜態分析過的初始值undefined,在最後等待file0.js和file1.js執行完畢之後, 才輸出file0.js導出的布爾值;

 

 

 

  爲何會這樣呢? 我好像在這邊找到了答案:http://exploringjs.com/es6/ch_modules.html#_modules ,ES6的import和export被提早到js的最頂層, 在函數或者對象,或者基本值被導出去的時候提早被靜態分析過,參考:http://www.ecma-international.org/ecma-262/6.0/#sec-parsemodule , http://www.ecma-international.org/ecma-262/6.0/#sec-toplevelmoduleevaluationjob

 

  結論:用ES6的export導出數據接口的時候, 最好統一用函數, 避免在循環依賴的時候, 由於JS會把不一樣類型的對象靜態解析成不一樣的初始值;

 

1

 

1

 

 

  瀏覽器兼容:

 

    chrome瀏覽器目前不支持import,和export;

 

    火狐的支持也有限, 比chrome好;

 

    我都用babel;

相關文章
相關標籤/搜索