国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

ES6 的模塊系統(tǒng)

MudOnTire / 471人閱讀

摘要:的模塊系統(tǒng)被設(shè)計成讓你可以一次性引入多個變量。動態(tài)靜態(tài),或者說規(guī)矩和如何打破規(guī)矩作為一門動態(tài)編程語言,令人驚訝地擁有一個靜態(tài)的模塊系統(tǒng)。只要你的需求都是靜態(tài)的話,這個模塊系統(tǒng)還是很的。

此文為翻譯,原文地址在這兒:https://hacks.mozilla.org/2015/08/es6-in-depth-modules/

ES6 是 ECMAScript 第 6 版本的簡稱,這是新一代的 JavaScript 的標準。ES6 in Depth 是關(guān)于 ES6 的一系列新特性的介紹。

遙想 2007 年,筆者開始在 Mozilla 的 JavaScript 團隊工作的時候,那個時候典型的 JavaScript 程序只有一行代碼。

兩年之后, Google Map 被發(fā)布。但是在那之前不久,JavaScript 的主要用途還是表單驗證,當然啦,你的處理器平均來說只有一行。

事過情遷,JavaScript 項目已經(jīng)變得十分龐大,社區(qū)也發(fā)展出了一些有助于開發(fā)可擴展程序的工具。首先你需要的便是模塊系統(tǒng)。模塊系統(tǒng)讓你得以將你的工作分散在不同的文件和目錄中,讓它們之前得以互相訪問,并且可以非常有效地加載它們。自然而然地,JavaScript 發(fā)展出了模塊系統(tǒng),事實上是多個模塊系統(tǒng)(AMD,CommonJS,CMD,譯者注)。不僅如此,社區(qū)還提供了包管理工具(NPM,譯者注),讓你可以安裝和拷貝高度依賴其他模塊的軟件。也許你會覺得,帶有模塊特性的 ES6,來得有些晚了。

模塊基礎(chǔ)

一個 ES6 的模塊是一個包含了 JS 代碼的文件。ES6 里沒有所謂的 module 關(guān)鍵字。一個模塊看起來就和一個普通的腳本文件一樣,除了以下兩個區(qū)別:

ES6 的模塊自動開啟嚴格模式,即使你沒有寫 "use strict"

你可以在模塊中使用 importexport

讓我們先來看看 export。在模塊中聲明的任何東西都是默認私有的,如果你想對其他模塊 Public,你必須 export 那部分代碼。我們有幾種實現(xiàn)方法,最簡單的方式是添加一個 export 關(guān)鍵字。

// kittydar.js - Find the locations of all the cats in an image.
// (Heather Arthur wrote this library for real)
// (but she didn"t use modules, because it was 2013)

export function detectCats(canvas, options) {
  var kittydar = new Kittydar(options);
  return kittydar.detectCats(canvas);
}

export class Kittydar {
  ... several methods doing image processing ...
}

// This helper function isn"t exported.
function resizeCanvas() {
  ...
}
...

你可以在 functionclassvarletconst 前添加 export

如果你想寫一個模塊,有這些就夠了!再也不用把代碼放在 IIFE 或者一個回調(diào)函數(shù)里了。既然你的代碼是一個模塊,而非腳本文件,那么你生命的一切都會被封裝進模塊的作用域,不再會有跨模塊或跨文件的全局變量。你導出的聲明部分則會成為這個模塊的 Public API。

除此之外,模塊里的代碼和普通代碼沒啥大區(qū)別。它可以訪問一些基本的全局變量,比如 ObjectArray。如果你的模塊跑在瀏覽器里,它將可以訪問 documentXMLHttpRequest

在另外一個文件中,我們可以導入這個模塊并且使用 detectCats() 函數(shù):

// demo.js - Kittydar demo program

import {detectCats} from "kittydar.js";

function go() {
    var canvas = document.getElementById("catpix");
    var cats = detectCats(canvas);
    drawRectangles(canvas, cats);
}

要導入多個模塊中的接口,你可以這樣寫:

import {detectCats, Kittydar} from "kittydar.js";

當你運行一個包含 import 聲明的模塊,被引入的模塊會先被導入并加載,然后根據(jù)依賴關(guān)系,每一個模塊的內(nèi)容會使用深度優(yōu)先的原則進行遍歷。跳過已經(jīng)執(zhí)行過的模塊,以此避免依賴循環(huán)。

這便是模塊的基礎(chǔ)部分,挺簡單的。

導出表

如果你覺得在每個要導出的部分前都寫上 export 很麻煩,你可以只寫一行你想要導出的變量列表,再用花括號包起來。

export {detectCats, Kittydar};

// no `export` keyword required here
function detectCats(canvas, options) { ... }
class Kittydar { ... }

導出表不一定要出現(xiàn)在文件的第一行,它可以出現(xiàn)在模塊頂級作用域中的任何一行。你可以寫多個導出表,也可以在列表中再寫上其他 export 聲明,只要沒有變量名被重復導出即可。

重名命導出和導入

如果導入的變量名恰好和你模塊中的變量名沖突了,ES6 允許你給你導入的東西重命名:

// suburbia.js

// Both these modules export something named `flip`.
// To import them both, we must rename at least one.
import {flip as flipOmelet} from "eggs.js";
import {flip as flipHouse} from "real-estate.js";
...

類似地,你在導出變量的時候也能重命名。這個特性在你想將同一個變量名導出兩次的場景下十分方便,舉個栗子:

// unlicensed_nuclear_accelerator.js - media streaming without drm
// (not a real library, but maybe it should be)

function v1() { ... }
function v2() { ... }

export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};
默認導出

新一代的標準的設(shè)計理念是兼容現(xiàn)有的 CommonJSAMD 模塊。所以如果你有一個 Node 項目,并且剛剛執(zhí)行完 npm install lodash,你的 ES6 代碼可以獨立引入 Lodash 中的函數(shù):

import {each, map} from "lodash";

each([3, 2, 1], x => console.log(x));

然而如果你已經(jīng)習慣了 _.each 或者看不見 _ 的話就渾身難受,當然這樣使用 Lodash 也是不錯的方式。

這種情況下,你可以稍微改變一下你的 import 寫法,不寫花括號:

import _ from "lodash";

這個簡寫等價于 import {default as _} from "lodash";。所有 CommonJS 和 AMD 模塊在被 ES6 代碼使用的時候都已經(jīng)有了默認的導出,這個導出和你在 CommonJS 中 require() 得到的東西是一樣的,那就是 exports 對象。

ES6 的模塊系統(tǒng)被設(shè)計成讓你可以一次性引入多個變量。但對于已經(jīng)存在的 CommonJS 模塊來說,你能得到的只有默認導出。舉個栗子,在撰寫此文之時,據(jù)筆者所知,著名的 colors 模塊并未特意支持 ES6。這是一個由多個 CommonJS 模塊組成的模塊,正如 npm 上的那些包。然而你依然可以直接將其引入到你的 ES6 代碼中。

// ES6 equivalent of `var colors = require("colors/safe");`
import colors from "colors/safe";

如果你想寫自己的默認導出,那也很簡單。這里面并沒有什么高科技,它和普通的導出沒什么兩樣,除了它的導出名是 default。你可以使用我們之前已經(jīng)介紹過的語法:

let myObject = {
  field1: value1,
  field2: value2
};
export {myObject as default};

這樣更好:

export default {
  field1: value1,
  field2: value2
};

export default 關(guān)鍵字后可以跟隨任何值:函數(shù),對象,對象字面量,任何你能說得出的東西。

模塊對象

抱歉,這篇文章的內(nèi)容有點多,但 JavaScript 已經(jīng)算好的了:因為一些原因,所有語言的模塊系統(tǒng)都有一大堆沒什么卵用的特性。所幸的是,咱們只有一個話題要討論了,呃,好吧,兩個。

import * as cows from "cows";

當你 import *,被引入進來的是一個 module namespace object。它的屬性是那個模塊的導出,所以如果 “cows” 模塊導出了一個名為 moo() 的函數(shù),當你像這樣引入了 “cows” 之后,你可以這樣寫 cows.moo()

聚合模塊

有時候一個包的主模塊會引入許多其他模塊,然后再將它們以一個統(tǒng)一的方式導出。為了簡化這樣的代碼,我們有一個 import-and-export 的簡寫方法:

// world-foods.js - good stuff from all over

// import "sri-lanka" and re-export some of its exports
export {Tea, Cinnamon} from "sri-lanka";

// import "equatorial-guinea" and re-export some of its exports
export {Coffee, Cocoa} from "equatorial-guinea";

// import "singapore" and export ALL of its exports
export * from "singapore";

這種 export-from 的表達式和后面跟了一個 exportimport-from 表達式類似。但和真正的導入不同,它并不會在你的作用域中加入二次導出的變量綁定。所以如果你打算在 world-foods.js 寫用到了 Tea 的代碼,就別使用這個簡寫形式。

如果 "singapore" 導出的某一個變量恰巧和其他的導出變量名沖突了,那么這里就會出現(xiàn)一個錯誤。所以你應該謹慎使用 export *

Whew!我們介紹完語法了,接下來進入有趣的環(huán)節(jié)。

import 到底干了啥

啥也沒干,信不信由你。

噢,你好像看起來沒那么好騙。好吧,那你相信標準幾乎沒有談到 import 該做什么嗎?你認為這是一件好事還是壞事呢?

ES6 將模塊的加載細節(jié)完全交給了實現(xiàn),其余的執(zhí)行部分則規(guī)定得非常詳細。

大致來說,當 JS 引擎運行一個模塊的時候,它的行為大致可歸納為以下四步:

解析:引擎實現(xiàn)會閱讀模塊的源碼,并且檢查是否有語法錯誤。

加載:引擎實現(xiàn)會(遞歸地)加載所有被引入的模塊。這部分咱還沒標準化。

鏈接:引擎實現(xiàn)會為每個新加載的模塊創(chuàng)建一個作用域,并且將模塊中的聲明綁定填入其中,包括從其他模塊中引入的。

當你嘗試 import {cake} from "paleo" 但是 “paleo” 模塊并沒有導出叫 cake 的東西時候,你也會在此時得到錯誤。這很糟糕,因為你離執(zhí)行 JS,品嘗 cake 只差一步了!

執(zhí)行:終于,JS 引擎開始執(zhí)行剛加載進來的模塊中的代碼。到這個時候,import 的處理過程已經(jīng)完成,因此當 JS 引擎執(zhí)行到一行 import 聲明的時候,它啥也不會干。

看到了不?我說了 import “啥也沒干”,沒騙你吧?有關(guān)編程語言的嚴肅話題,哥從不說謊。

不過,現(xiàn)在咱們可以介紹這個體系中有趣的部分了,這是一個非常酷的 trick。正因為這個體系并沒有指定加載的細節(jié),也因為你只需要看一眼源碼中的 import 聲明就可以在運行前搞清楚模塊的依賴,某些 ES6 的實現(xiàn)甚至可以通過預處理就完成所有的工作,然后將模塊全部打包成一個文件,最后通過網(wǎng)絡(luò)分發(fā)。像 webpack 這樣的工具就是做這個事情的。

這非常的了不起,因為通過網(wǎng)絡(luò)加載資源是非常耗時的。假設(shè)你請求一個資源,接著發(fā)現(xiàn)里面有 import 聲明,然后你又得請求更多的資源,這又會耗費更多的時間。一個 naive 的 loader 實現(xiàn)可能會發(fā)起許多次網(wǎng)絡(luò)請求。但有了 webpack,你不僅可以在今天就開始使用 ES6,還可以得到一切模塊化的好處并且不向運行時性能妥協(xié)。

原先我們計劃過一個詳細定義的 ES6 模塊加載規(guī)范,而且我們做出來了。它沒有成為最終標準的原因之一是它無法與打包這一特性調(diào)和。模塊系統(tǒng)需要被標準化,打包也不應該被放棄,因為它太好了。

動態(tài) VS 靜態(tài),或者說:規(guī)矩和如何打破規(guī)矩

作為一門動態(tài)編程語言,JavaScript 令人驚訝地擁有一個靜態(tài)的模塊系統(tǒng)。

importexport 只能寫在頂級作用域中。你無法在條件語句中使用引入和導出,你也不能在你寫的函數(shù)作用域中使用import

所有的導出必須顯示地指定一個變量名,你也無法通過一個循環(huán)動態(tài)地引入一堆變量。

模塊對象被封裝起來了,我們無法通過 polyfill 去 hack 一個新 feature。

在模塊代碼運行之前,所有的模塊都必須經(jīng)歷加載,解析,鏈接的過程。沒有可以延遲加載,惰性 import 的語法。

對于 import 錯誤,你無法在運行時進行 recovery。一個應用可能包含了幾百個模塊,其中的任何一個加載失敗或鏈接失敗,這個應用就不會運行。你無法在 try/catch 語句中 import。(不過正因為 ES6 的模塊系統(tǒng)是如此地靜態(tài),webpack 可以在預處理時就為你檢測出這些錯誤)。

你沒辦法 hook 一個模塊,然后在它被加載之前運行你的一些代碼。這意味著模塊無法控制它的依賴是如何被加載的。

只要你的需求都是靜態(tài)的話,這個模塊系統(tǒng)還是很 nice 的。但你還是想 hack 一下,是嗎?

這就是為啥你使用的模塊加載系統(tǒng)可能會提供 API。舉個栗子,webpack 有一個 API,允許你 “code splitting”,按照你的需求去惰性加載模塊。這個 API 也能幫你打破上面列出的所有規(guī)矩。

ES6 的模塊是非常靜態(tài)的,這很好——許多強大的編譯器工具因此收益。而且,靜態(tài)的語法已經(jīng)被設(shè)計成可以和動態(tài)的,可編程的 loader API 協(xié)同工作。

我何時能開始使用 ES6 模塊?

如果你今天就要開始使用,你需要諸如 Traceur 和 Babel 這樣的預處理工具。這個系列專題之前也有文章介紹了如何使用 Babel 和 Broccoli 去生成可用于 Web 的 ES6 代碼。那篇文章的栗子也被開源在了 GitHub 上。筆者的這篇文章也介紹了如何使用 Babel 和 webpack。

ES6 模塊系統(tǒng)的主要設(shè)計者是 Dave Herman 和 Sam Tobin-Hochstadt,此二人不顧包括筆者在內(nèi)的數(shù)位委員的反對,始終堅持如今你見到的 ES6 模塊系統(tǒng)的靜態(tài)部分,爭論長達數(shù)年。Jon Coppeard 正在火狐瀏覽器上實現(xiàn) ES6 的模塊。之后包括 JavaScript Loader 規(guī)范在內(nèi)的工作已經(jīng)在進行中。HTML 中類似

閱讀需要支付1元查看
<