ES2015: import和export

1、模塊化

前端模塊化的好處都已經被說爛了,概括爲兩點:html

  1. 避免全局變量污染;
  2. 有效的處理依賴關係。

ES2015終於引入了模塊的概念,最近學習了下,順便記下筆記。前端

2、準備工做

  1. 在Chrome瀏覽器環境運行代碼;
  2. 新建個目錄,目錄下包含兩個文件:
  • index.html
<!DOCTYPE html>
<html>
    <head></head>
    <body>
        <h1>import</h1>
        <script src="index.js" type="module"></script>
    </body>
</html>
  • index.js

3、模塊導出 export

  1. 一個文件定義一個模塊,經過export語句導出該模塊輸出的變量;
  2. export語句有兩種語法格式:命名導出默認導出

3.1 命名導出

命名導出就是明確導出的變量名稱和值。git

在目錄下建立math.js,內容以下:github

// Case 1: export後面跟變量輸出聲明語句
export var PI = 3.14;

// Case 2: export後面直接跟變量定義語句
export var add = function (x, y) { // 導出函數print
    return x + y;
}

這表示math.js模塊導出變量PIadd。用NodeJS的模塊格式可表示爲:瀏覽器

var PI = 3.14;
var add = function (x, y) { // 導出函數print
    return x + y;
}

module.exports.PI = PI; 
module.exports.add = add;

index.js內容:模塊化

import * as Math from "./math.js"; // import是導入模塊,後面會說。

console.log(Math.PI);
console.log(Math.add(1, 2));

用瀏覽器打開頁面,看看輸出結果是否OK:函數

3.14
3

若是導出多個變量,能夠採用簡寫格式:

// 調整math.js內容
var PI = 3.14;
var add = function (x, y) { 
    return x + y;
}

export { PI, add }; // 簡寫格式,統一列出須要輸出的變量

重命名導出變量

簡寫格式還能夠對輸出的變量重命名:學習

// 再次修改math.js
var PI = 3.14;

var add = function (x, y) { 
    return x + y;
}

export { PI, add as Add}; // 把輸出變量add重命名爲Add(注意不用雙引號)

經過關鍵字as把輸出變量add重命名爲Add(Add是個字面量,不是字符串不須要引號)。prototype

一樣在index.js模塊也要修改下:code

import * as Math from "./math.js";

console.log(Math.PI);
console.log(Math.Add(1, 2)); // Add方法名稱改動了。

export語句後面能夠跟什麼?

命名導出須要同時指定導出的變量名稱和變量值,因此export語句後面跟的通常是:

  • 變量(函數)聲明語句;
  • export簡寫格式。

不能夠是表達式,由於表達式只有值,沒有名字。

// 語法錯誤:Declaration or statement expected
export 3.14

3.2 默認導出

經過關鍵字default修飾export能夠指定一個模塊的默認輸出變量值(在導入模塊的默認輸出時,不須要指定導出變量名稱,這個後面再說)。

// Case 3 常量
export default 25; 

// Case 4 變量
var PI = 3.14;
export default PI 

// Case 5 函數
export default function add2( x, y) {
    return x + y;
}
  1. 一個模塊最多隻能有一個默認導出;
  2. 默認輸出能夠視爲名字是default的模塊輸出變量;
  3. 默認導出後面能夠是表達式,由於它只須要值。
export default 3.14

3.3 總結:

  1. 一個文件一個模塊;
  2. export命名導出;
  3. export default默認導出;
  4. export語句必須在模塊做用域的最頂層,即export不能夠出如今任意花括號內,如函數語句裏,子代碼塊裏(控制語句)。

4、模塊導入

經過import語句導入外部模塊。對應export語句的兩種導出方式,import也分別存在兩種不一樣的模塊導入語法格式。

4.1 導入模塊的命名輸出

修改index.js:

import { PI, Add } from './math.js';
console.log(PI);
console.log(Add(1, 2));

表示:導入math.js模塊裏輸出的變量PI, Add
注意名稱必需要和math.js模塊的輸出變量一一對應,不然就是undefined

重命名導入的變量

該格式還支持對導入的變量重命名:

import { PI as pi, Add as add} from './math.js';

通配符*

若是導入一個模塊全部命名輸出,可採用通配符*

// 修改index.js
import * as Math from './math.js'; // 此時必須經過as指定個別名
console.log(Math.PI);
console.log(Math.Add(1, 2));

表示導入模塊math.js全部命名輸出變量,並經過Math變量訪問全部命名導出的變量。
Math變量是個特殊的對象,叫模塊對象

Object.prototype.toString.call(Math); // [object Module]

而且這個對象和它的屬性都是隻讀的(後面細說)。

4.2 導入模塊的默認輸出

// 修改math.js:
var PI = 3.14;

var add = function (x, y) { 
    return x + y;
}

export { PI, add as Add }; // 簡寫格式,統一列出須要輸出的變量

export default function say() { // 默認輸出
    console.log("I am default export");
}

修改index.js:

import say  from "./math.js";
say();
  1. 表示導入模塊math.js的默認輸出,此時能夠用as重命名;
    能夠利用重命名方式避免導入模塊的變量名稱和本模塊變量命名衝突。
  2. 導入模塊默認輸出的名字能夠任意命名。

    import say2  from "./math.js"; // 
    say2();
  3. 若是同時導入模塊的命名輸出和默認輸出,可採用格式:
import say, * as Math from './math.js'; 
// OR
import say, { PI, Add } from './math.js';

默認導入必定放在命名導入前面

// 非法
import * as Math, say from './math.js'; 

// 非法
import { PI, Add }, say from './math.js';

4.3 只導入

若是隻導入一個模塊,但不引用模塊的輸出變量,能夠簡寫爲:

import './math.js'

此時只會觸發模塊math.js的執行。

4.4 總結:

  1. import xx from "xxx"導入默認輸出;
  2. import { xx } from "xxx"導入指定的命名輸出;
  3. import * as xx from "xxx"導入所有命名輸出;
  4. import "xxx"只導入;
  5. 一樣import語句也必須在模塊的最頂層做用域裏,即import不能夠出如今任意花括號內,如函數語句裏,子代碼塊裏(控制語句)。
    注意在動態導入沒這個限制
  6. 模塊都是在嚴格模式下執行的。

5、修改導入/導出的變量值

5.1 修改導入的變量值

模塊能夠導出任何類型變量(引用變量和值變量),若是在模塊index.js裏修改了模塊math.js導出的引用變量或者值變量,那會影響模塊math.js裏的值麼?
很遺憾,import導入的變量和變量的屬性都是隻讀的,不能也不該該修改引入的變量值。

import * as Math from './math.js'; 
// TypeError: Cannot assign to read only property 'Count' of object '[object Module]'
Math.Count = 12;

5.2 修改導出的變量值

反過來想一想,若是模塊math.js修改了其導出的引用變量或者值變量在,那會影響模塊index.js裏的取值麼?
修改math.js:

var Count = 0;
var increase =  function() {
    Count++;
}
var Person = {
    name: 'Bob'
}
var changeName = function() {
    Person.name = 'John';
}

export { Count, Person, increase, changeName };

修改index.js:

import * as Math from './math.js'; 

console.log(`Person:${JSON.stringify(Math.Person)}, Count:${Math.Count}`);// 修改前
Math.increase();
Math.changeName();
console.log(`Person:${JSON.stringify(Math.Person)}, Count:${Math.Count}`);//  修改後

輸出:

Person:{"name":"Bob"}, Count:0
Person:{"name":"John"}, Count:1

從輸出能夠看出只要math.js修改了導出值就會影響index.js的取值。

5.3 小結

摘自MDN import

The static import statement is used to import read only live bindings which are exported by another module
  1. read only:引用方修改導入模塊的變量值不影響原模塊的變量值;
  2. live bindings:模塊修改了其輸出變量的值會影響其餘引入模塊的取值。

6、再看export語句

瞭解了exportimport基本用法後,咱們再看看export語句另外一個語法規則:導出引入的模塊的變量。
上面的例子裏export語句都是導出模塊自身定義的變量,其實export還能夠導出模塊引入模塊的輸出。
在目錄添加文件log.js:

export var Log = function(msg) {
    console.log(msg);
}
export default 'I am log.js';

修改math.js:

var Count = 0;
var increase =  function() {
    Count++;
}
var Person = {
    name: 'Bob'
}

var changeName = function() {
    Person.name = 'John';
}

export { Count, Person, increase, changeName}; 
export default function say() {
    console.log(`Person:${JSON.stringify(Person)}, Count:${Count}`)
}
export * from './log.js'; //

修改index.js:

import say, * as Math from './math.js'; 

Math.Log('hello'); // 該方法來之log.js模塊

查看下輸出是否OK。

其中export * from './log.js';表示導出模塊log.js全部的命名輸出。等價於:

export { Log } from './log.js';

注意: 這種語法格式export * from './log.js';不能夠定義別名,而花括號的語法格式export { Log } from './log.js'能夠定義別名,即:

export { Log as log} from './log.js'

怎麼在math.js模塊裏導出模塊log.js的默認輸出呢?
只能採用先導入,再導出方式:

import logName from './log.js';
export { logName }

參考

整理自gitHub筆記ES2015模塊 & import, export

相關文章
相關標籤/搜索