本系列屬於阮一峯老師所著的ECMAScript 6 入門學習筆記javascript
在ES6以前,JavaScript一直沒有模塊(module)體系,沒法將一個大程序拆分紅互相依賴的小文件,再用簡單的方法拼裝起來。社區制定了一些模塊加載方案,最主要的有CommonJS和AMD兩種。前者用於服務器,後者用於瀏覽器。java
ES6在語言標準的層面上,實現了模塊功能,並且實現很簡單,能夠徹底取代CommmJS和AMD規範,成爲瀏覽器和服務器通用的模塊解決方案。es6
因爲 ES6 模塊是編譯時加載,使得靜態分析成爲可能。有了它,就能進一步拓寬 JavaScript 的語法,好比引入宏(macro)和類型檢驗(type system)這些只能靠靜態分析實現的功能。瀏覽器
ES6 的模塊自動採用嚴格模式,無論你有沒有在模塊頭部加上"use strict";
。服務器
嚴格模式主要有如下限制。異步
with
語句delete prop
,會報錯,只能刪除屬性delete global[prop]
eval
不會在它的外層做用域引入變量eval
和arguments
不能被從新賦值arguments
不會自動反映函數參數的變化arguments.callee
arguments.caller
this
指向全局對象fn.caller
和fn.arguments
獲取函數調用的堆棧protected
、static
和interface
)模塊功能主要由兩個命令構成:export
和import
。export
命令用於規定模塊的對外接口,import
命令用於輸入其餘模塊提供的功能。函數
一個模塊就是一個獨立的文件。該文件內部的全部變量,外部沒法獲取。學習
// 對外輸出變量 export var firstName = 'Angus' export var lastName = 'jay' export var year = 1998 // 另外一種寫法 var firstName = 'Angus' var lastName = 'jay' var year = 1998 export {firstName,lastName,year} // 優先考慮這種寫法。能夠在腳本尾部清除看出輸出變量 // 除了輸出變量,還能夠輸出函數或類(class) export function multiply(x,y){ retrun x * y } // 一般export輸出變量就是原本的名字,但能夠用as關鍵字重命名 export { multiply as stream1, multiply as stream2 } // multiply能夠用兩個不一樣的名字輸出兩次
須要特別注意的是,export
命令規定的是對外的接口,必須與模塊內部的變量創建一一對應關係。ui
// 報錯 export 1 // 報錯 var m = 1 export m // 以上兩種寫法,沒有提供對外的接口,都是直接輸出1。只是一個值,不是接口 // 寫法一 export var m = 1 // 寫法二 var m = 1 export {m} // 寫法三 var n = 1 export {n as m} // 一樣對function和class的輸出也必須遵照接口名與模塊變量一一對應的關係 // 報錯 function f(){} export f // 正確 export function f(){} // 正確 function f(){} export {f}
另外,export
語句輸出的接口,與其對應的值是動態綁定關係,即經過該接口,能夠取到模塊內部實時的值。this
export var foo = 'bar' setTimeout(() => foo='baz',500)
使用export
命令定義了模塊的對外接口後,其餘JS文件就能夠經過import
命令加載這個模塊。
import {firstName,lastName,year} from './profile' function setName(element){ element.textContent = firstName + '' + lastName } // 使用as關鍵字將輸入的變量重命名 import {lastName as surname} from './profile' // import後面的from指定模塊文件的位置,可使相對路徑也能夠是絕對路徑,.js後綴能夠省略 // import命名具備提高效果,會提高到整個模塊的頭部,首先執行 foo() import {foo} from 'my_module' // 因爲import是靜態執行,因此不能使用表達式和變量,這些只有在運行時才能獲得結果的語法結構 // 報錯 import { 'f' + 'oo' } from 'my_module' // 報錯 if(x === 1){ import { foo } from 'module1' }else{ import { foo } from 'module2' }
import
語句會執行所加載的模塊,所以能夠有如下寫法
import 'lodash' // 屢次重複執行同一句import語句,那麼只會執行一次 import 'lodash' import 'lodash' // import語句是Singleton模式 import {foo} from 'my_module' import {bar} from 'my_module' // 等同於 import {foo,bar} from 'my_module'
目前階段,經過 Babel 轉碼,CommonJS 模塊的require
命令和 ES6 模塊的import
命令,能夠寫在同一個模塊裏面,可是最好不要這樣作。由於import
在靜態解析階段執行,因此它是一個模塊之中最先執行的。
除了指定加載某個輸出值,還可使用總體加載,即用星號*
指定一個對象,全部輸出值都加載在這個對象上。
import * as circle from './circle' // 注意:模塊總體加載所在的對象,應該是能夠靜態分析的,因此不容許運行時改變 circle.foo = 'hello' circle.area = function(){}
爲了使用方便,在不須要用戶知道所要加載的變量名或者函數名時就能加載模塊,須要使用到export default
命名,爲模塊指定默認輸出。
export default function (){ console.log('foo') } // 加載時,import命令能夠爲該匿名函數指定任意名字 import customName from './export-default' customName() // 'foo' // export default命令用在非匿名函數前也能夠 export default function foo(){} // 另外一種寫法 function foo(){} export default foo
export default
命令用於指定模塊的默認輸出。顯然,一個模塊只能有一個默認輸出,所以export default
命令只能使用一次。因此,import
命令後面纔不用加大括號,由於只可能對應一個方法。
本質上,export default
就是輸出一個叫作default
的變量或方法,而後系統容許你爲它取任意名字。
function add(){} export {add as default} // 等同於 export default add import {default as foo} from 'modules' // 等同於 import foo from 'modules'
一樣地,由於export default
本質是將該命令後面的值,賦給default
變量之後再默認,因此直接將一個值寫在export default
以後。
// 正確 export default 10 // 報錯 export 10
能夠在一條import
語句中,同時輸入默認方法和其餘接口
import _,{each,each as forEach} from 'lodash' // 對應的export語句 export default function(){} export function each(){}
export default
也能夠用來輸出類。
export default class{} import MyClass from 'myClass' let o = new MyClass()
// 在一個模塊中,同時輸入輸出同一個模塊 export {foo,bar} from 'my_module' // 等同於 import {foo,bar} from 'my_module' export {foo,bar} // 接口更名與總體輸出也能夠採用這種寫法 export {foo as myFoo} from 'my_module' export * from 'my_module'
import
命令會被 JavaScript 引擎靜態分析,先於模塊內的其餘模塊執行(叫作」鏈接「更合適)。import
和export
命令只能在模塊的頂層,不能在代碼塊之中。這樣的設計致使沒法在運行時加載模塊。在語法上,條件加載就不可能實現。
若是import
命令要取代 Node 的require
方法,這就造成了一個障礙。由於require
是運行時加載模塊,import
命令沒法取代require
的動態加載功能。
// 報錯 if (x === 2) { import MyModual from './myModual'; } // require加載的模塊只有運行時才知道,import語句作不到這一點 const path = './' + fileName const myModual = require(path)
在這種狀況下,產生一個提案,建議引入import()
函數,完成動態加載。import()
返回一個Promise對象完成異步加載。這樣就相似於Node的require
方法,不一樣於require
方法的同步加載。