ES6 module 簡單整理

16.11.11node

過了又一個關棍節,感受。。。。很差es6

參考:阮一峯ES6緩存

CommonJS 的模塊規範

CommonJS對模塊的定義很是簡單,主要分爲模塊引用、模塊定義和模塊標誌babel

模塊的引用

var math = reqiure('math');

require()接受模塊的標誌符。以此引入一個模塊的API到當前的上下文中。函數

模塊的定義

上下文提供exports對象用於導出當前模塊的方法和變量,而且它是惟一導出的出口。
module對象,它表明模塊自身。而exports是module的屬性。ui

模塊的標識

模塊標識就是傳遞給require()方法的參數。必須符合小駝峯命名的字符串。設計

CommonJS的模塊導出和引用機制使得用戶徹底沒必要考慮變量污染。code

ES6 module

ES6模塊的設計思想,是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。CommonJS和AMD模塊,都只能在運行時肯定這些東西。對象

// CommonJS模塊
let { stat, exists, readFile } = require('fs');

// 等同於 這種方式稱爲「運行時加載」
let _fs = require('fs');
let stat = _fs.stat, exists = _fs.exists, readfile = _fs.readfile;

exports

exports && module.exports 繼承

直接複製給exports,會報錯。

exports= function(){
    //
}

由於exports對象是經過形參的方式傳入的,直接賦值形參會改變形參的做用,但並不能改變做用域外的值。

若是要達到require引入一個類的效果,請賦值給module.exports對象,這個迂迴的方案不改變形參的引用。

export

須要特別注意的是,export命令規定的是對外的接口,必須與模塊內部的變量創建一一對應關係。

import

使用export命令定義了模塊的對外接口之後,其餘JS文件就能夠經過import命令加載這個模塊(文件)。
import命令接受一個對象(用大括號表示),裏面指定要從其餘模塊導入的變量名。大括號裏面的變量名,必須與被導入模塊s對外接口的名稱相同。

模塊的總體加載

除了指定加載某個輸出值,還可使用總體加載,即用星號(*)指定一個對象,全部輸出值都加載在這個對象上面。

export default

export default 一個模塊只有一個。爲模塊指定默認輸出。

使用import命令的時候,用戶須要知道所要加載的變量名或函數名,不然沒法加載。可是,用戶確定但願快速上手,未必願意閱讀文檔,去了解模塊有哪些屬性和方法。

其餘模塊加載該模塊時,import命令能夠爲該匿名函數指定任意名字。

如下比較默認輸出和正常輸出:

// 輸出
export default function crc32() {
  // ...
}
// 輸入
import crc32 from 'crc32';

// 輸出
export function crc32() {
  // ...
};
// 輸入
import {crc32} from 'crc32';

本質上,export default就是輸出一個叫作default的變量或方法,而後系統容許你爲它取任意名字。

模塊的繼承

ES6模塊加載的實質

CommonJS模塊的是一個值的拷貝,而ES6模塊輸出的是值的引用。

ES6模塊加載的機制,與CommonJS模塊徹底不一樣。CommonJS模塊輸出的是一個值的拷貝,而ES6模塊輸出的是值的引用。

CommonJS模塊輸出的是被輸出值的拷貝,也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值。

ES6模塊的運行機制與CommonJS不同,它遇到模塊加載命令import時,不會去執行模塊,而是隻生成一個動態的只讀引用。等到真的須要用到時,再到模塊裏面去取值,換句話說,ES6的輸入有點像Unix系統的「符號鏈接」,原始值變了,import輸入的值也會跟着變。所以,ES6模塊是動態引用,而且不會緩存值,模塊裏面的變量綁定其所在的模塊。

循環加載

CommonJS的加載原理

CommonJS的一個模塊,就是一個腳本文件。require命令第一次加載該腳本,就會執行整個腳本,而後在內存生成一個對象。

{
      id: '...',
      exports: { ... },
      loaded: true,
      ...
   }

該對象的id屬性是模塊名,exports屬性是模塊輸出的各個接口,loaded屬性是一個布爾值,表示該模塊的腳本是否執行完畢。

之後須要用到這個模塊的時候,就會到exports屬性上面取值。即便再次執行require命令,也不會再次執行該模塊,而是到緩存之中取值。也就是說,CommonJS模塊不管加載多少次,都只會在第一次加載時運行一次,之後再加載,就返回第一次運行的結果,除非手動清除系統緩存。

<mark>CommonJS模塊的重要特性是加載時執行,即腳本代碼在require的時候,就會所有執行。一旦出現某個模塊被"循環加載",就只輸出已經執行的部分,還未執行的部分不會輸出。</mark>

// a.js
 exports.done = false;
var b = require('./b.js');
console.log('在 a.js 之中,b.done = %j', b.done);
exports.done = true;
console.log('a.js 執行完畢');

//b.js   
exports.done = false;
var a = require('./a.js');
console.log('在 b.js 之中,a.done = %j', a.done);
exports.done = true;
console.log('b.js 執行完畢');

//main.js
var a = require('./a.js');
var b = require('./b.js');
console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done);

//執行main.js
$ node main.js

在 b.js 之中,a.done = false
b.js 執行完畢
在 a.js 之中,b.done = true
a.js 執行完畢
在 main.js 之中, a.done=true, b.done=true

ES6處理「循環加載」與CommonJS有本質的不一樣。ES6模塊是動態引用,若是使用import從一個模塊加載變量(即import foo from 'foo'),那些變量不會被緩存,而是成爲一個指向被加載模塊的引用,須要開發者本身保證,真正取值的時候可以取到值。

// a.js以下
import {bar} from './b.js';
console.log('a.js');
console.log(bar);
export let foo = 'foo';

// b.js
import {foo} from './a.js';
console.log('b.js');
console.log(foo);
export let bar = 'bar';

$ babel-node a.js
b.js
undefined
a.js
bar

跨模塊常量

上面說過,const聲明的常量只在當前代碼塊有效。若是想設置跨模塊的常量(即跨多個文件),能夠採用下面的寫法。

// constants.js 模塊
export const A = 1;
export const B = 3;
export const C = 4;

// test1.js 模塊
import * as constants from './constants';
console.log(constants.A); // 1
console.log(constants.B); // 3

// test2.js 模塊
import {A, B} from './constants';
console.log(A); // 1
console.log(B); // 3
相關文章
相關標籤/搜索