【早讀匯】上海@jean-lee《ES6標準入門》筆記

【早讀匯】上海@jean-lee《ES6標準入門》筆記

前言

本文系早讀匯第三期活動來自上海@jean-lee 的讀書筆記。


正文從這開始~


Module


ES6 模塊的設計思想,是儘量的靜態化,使得編譯時就能確定模塊的依賴關係,以及輸入和輸出的變量。 ES6 模塊不是對象,而是通過export命令顯式指定輸出的代碼,再通過import命令輸入。

圖片


ES6 模塊的優點:

  • 靜態加載

  • 不再需要UMD模塊格式了,將來服務器和瀏覽器都會支持 ES6 模塊格式。目前,通過各種工具庫,其實已經做到了這一點。

  • 將來瀏覽器的新 API 就能用模塊格式提供,不再必須做成全局變量或者navigator對象的屬性。 不再需要對象作爲命名空間(比如Math對象),未來這些功能可以通過模塊提供。


嚴格模式


嚴格模式主要有以下限制:

  • 變量必須聲明後再使用

  • 函數的參數不能有同名屬性,否則報錯

  • 不能使用with語句

  • 不能對只讀屬性賦值,否則報錯

  • 不能使用前綴0表示八進制數,否則報錯

  • 不能刪除不可刪除的屬性,否則報錯

  • 不能刪除變量delete prop,會報錯,只能刪除屬性delete global[prop]

  • eval不會在它的外層作用域引入變量

  • eval和arguments不能被重新賦值

  • arguments不會自動反映函數參數的變化

  • 不能使用arguments.callee

  • 不能使用arguments.caller

  • 禁止this指向全局對象

  • 不能使用fn.caller和fn.arguments獲取函數調用的堆棧

  • 增加了保留字(比如protected、static和interface)


export命令


模塊功能主要由兩個命令構成:export和import。export命令用於規定模塊的對外接口,import命令用於輸入其他模塊提供的功能。


一個模塊就是一個獨立的文件。該文件內部的所有變量,外部無法獲取。如果你希望外部能夠讀取模塊內部的某個變量,就必須使用export關鍵字輸出該變量。下面是一個 JS 文件,裏面使用export命令輸出變量。

圖片

上面代碼在export命令後面,使用大括號指定所要輸出的一組變量。它與前一種寫法(直接放置在var語句前)是等價的,但是應該優先考慮使用這種寫法。因爲這樣就可以在腳本尾部,一眼看清楚輸出了哪些變量。


export命令除了輸出變量,還可以輸出函數或類(class)。

圖片

上面代碼對外輸出一個函數multiply。

圖片

上面代碼使用as關鍵字,重命名了函數v1和v2的對外接口。重命名後,v2可以用不同的名字輸出兩次。


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


// 報錯 export 1;

// 報錯 var m = 1; export m; 上面兩種寫法都會報錯,因爲沒有提供對外的接口圖片


export語句輸出的接口,與其對應的值是動態綁定關係,即通過該接口,可以取到模塊內部實時的值。

圖片

上面代碼輸出變量foo,值爲bar,500毫秒之後變成baz。

export命令可以出現在模塊的任何位置,只要處於模塊頂層就可以。如果處於塊級作用域內,就會報錯,因爲沒法做靜態優化。


important命令


通過import命令加載使用export命令定義了的模塊的對外接口

圖片

import命令接受一對大括號,裏面指定要從其他模塊導入的變量名。大括號裏面的變量名,必須與被導入模塊(profile.js)對外接口的名稱相同。

圖片


如果只是模塊名,不帶有路徑,那麼必須有配置文件,告訴 JavaScript 引擎該模塊的位置。

圖片


util是模塊文件名,由於不帶有路徑,必須通過配置,告訴引擎怎麼取到這個模塊。


import命令具有提升效果,會提升到整個模塊的頭部,首先執行。

圖片


上面的代碼不會報錯,因爲import的執行早於foo的調用。這種行爲的本質是,import命令是編譯階段執行的,在代碼運行之前。


由於import是靜態執行,所以不能使用表達式和變量,這些只有在運行時才能得到結果的語法結構。

圖片


import語句會執行所加載的模塊,因此可以有下面的寫法。

圖片


上面代碼僅僅執行lodash模塊,但是不輸入任何值。


如果多次重複執行同一句import語句,那麼只會執行一次,而不會執行多次。 import語句是 Singleton 模式

圖片


模塊的整體加載


除了指定加載某個輸出值,還可以使用整體加載,即用星號(*)指定一個對象,所有輸出值都加載在這個對象上面

圖片

export default 命令

爲了給用戶提供方便,讓他們不用閱讀文檔就能加載模塊,就要用到export default命令,爲模塊指定默認輸出。


// export-default.js export default function () { console.log('foo'); }


其他模塊加載該模塊時,import命令可以爲該匿名函數指定任意名字。此時import命令後面,不使用大括號。

圖片

export default命令用在非匿名函數前,也是可以的。


export default命令用於指定模塊的默認輸出。顯然,一個模塊只能有一個默認輸出,因此export default命令只能使用一次。所以,import命令後面纔不用加大括號,因爲只可能對應一個方法。


有了export default命令,輸入模塊時就非常直觀了,以輸入 lodash 模塊爲例。


import _ from 'lodash'; 如果想在一條import語句中,同時輸入默認方法和其他變量,可以寫成下面這樣。

圖片


對應上面代碼的export語句如下。

圖片


上面代碼的最後一行的意思是,暴露出forEach接口,默認指向each接口,即forEach和each指向同一個方法。


如果要輸出默認的值,只需將值跟在export default之後即可。

圖片


export 與 import 的複合寫法


如果在一個模塊之中,先輸入後輸出同一個模塊,import語句可以與export語句寫在一起。

模塊的接口改名和整體輸出,也可以採用這種寫法。

image.png

默認接口的寫法如下。

圖片

具名接口改爲默認接口的寫法如下。

圖片


同樣地,默認接口也可以改名爲具名接口。

圖片


模塊的繼承


假設有一個circleplus模塊,繼承了circle模塊。

圖片


上面代碼中的export *,表示再輸出circle模塊的所有屬性和方法。注意,export *命令會忽略circle模塊的default方法。然後,上面代碼又輸出了自定義的e變量和默認方法。


這時,也可以將circle的屬性或方法,改名後再輸出。

圖片


上面代碼表示,只輸出circle模塊的area方法,且將其改名爲circleArea。


加載上面模塊的寫法如下。

圖片

上面代碼中的import exp表示,將circleplus模塊的默認方法加載爲exp方法。


ES6模塊加載的實質


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


CommonJS模塊輸出的是被輸出值的拷貝,也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值。ES6模塊的運行機制與CommonJS不一樣,它遇到模塊加載命令import時,不會去執行模塊,而是隻生成一個動態的只讀引用。等到真的需要用到時,再到模塊裏面去取值,換句話說,ES6的輸入有點像Unix系統的“符號連接”,原始值變了,import輸入的值也會跟着變。因此,ES6模塊是動態引用,並且不會緩存值,模塊裏面的變量綁定其所在的模塊。


瀏覽器的模塊加載


瀏覽器使用 ES6 模塊的語法如下。


由於type屬性設爲module,所以瀏覽器知道這是一個 ES6 模塊。


對於外部的模塊腳本(上例是foo.js),有幾點需要注意。

  • 該腳本自動採用嚴格模塊。

  • 該腳本內部的頂層變量,都只在該腳本內部有效,外部不可見。

  • 該腳本內部的頂層的this關鍵字,返回undefined,而不是指向window。


循環加載


“循環加載”(circular dependency)指的是,a腳本的執行依賴b腳本,而b腳本的執行又依賴a腳本。 通常,“循環加載”表示存在強耦合,如果處理不好,還可能導致遞歸加載,使得程序無法執行,因此應該避免出現。


對於JavaScript語言來說,目前最常見的兩種模塊格式CommonJS和ES6,處理“循環加載”的方法是不一樣的,返回的結果也不一樣。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章