在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ 模塊
生成器
箭頭函數(shù)
繼續(xù)迭代器
未來前景
簡介
非結(jié)構(gòu)化賦值
迭代器和 for-of 循環(huán)
代理
符號
模版字符串
let 和 const
容器
模塊

模塊

回到 2007 年,那時我開始在 Mozilla 的 JavaScript 團隊工作,搞笑的是一個典型的 Javascript 程序長度是一行。

這是谷歌地圖發(fā)布后兩年。在那不久之前,JavaScript 的主要用途是表單驗證。的的確確,你的大部分<input onchange= >處理程序?qū)恰恍写a。

后來事情發(fā)生了改變。JavaScript 程序發(fā)展到了令人瞠目結(jié)舌的規(guī)模,社會也大力發(fā)展了與之相關(guān)的工作。你最需要的基本東西之一是一個模塊系統(tǒng),用來在多文件和多目錄之間傳遞你的工作的方式——但仍要確定你所有的代碼可以再需要的時候互相訪問——同時能夠高效地加載所有代碼。所以自然的,JavaScript 有了一個模塊系統(tǒng),確切的說是幾個。還有一些軟件包管理器,用于安裝所有軟件和處理高級別依賴的工具。你可能會覺得擁有新模塊語法的 ES6 出現(xiàn)的有些晚了。

好的,今天我們來看看 ES6 是否在現(xiàn)存系統(tǒng)上添加了東西,還有是否會在之上建立未來的標(biāo)準(zhǔn)和工具。但是首先,我們只是像潛水一樣來看看 ES6 模塊是什么樣的。

模塊基礎(chǔ)

一個 ES6 模塊是一個包含了 JS 代碼的文件。沒有特殊的模塊關(guān)鍵詞;一個模塊讀取起來最多只像一個腳本。有兩個不同。

  • ES6 模塊會自動嚴(yán)格模式代碼,盡管你沒有在其中寫“use strict”。

  • 你可以使用導(dǎo)入和導(dǎo)出模塊。

我們先來談?wù)剬?dǎo)出。在默認(rèn)情況下,模塊內(nèi)聲明的一切都是在本地對該模塊而言。如果你想讓模塊中聲明的東西成為公開的,那么其他模塊就可以使用它,你必須導(dǎo)出該功能。這里有幾種方式,最簡單的方式是添加導(dǎo)出關(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() {
  ...
}
...

你可以導(dǎo)出任何頂層函數(shù),類,變量,let 或 const。

這就是你編寫一個模塊所需要知道的所有!你不必把所有都放到一個 IIFE 或回調(diào)上。只需繼續(xù)下去并聲明你所需要的所有事情。由于代碼是一個模塊而不是一個腳本,所有聲明都限于該模塊內(nèi),而不是在所有腳本和模塊中全局可見。導(dǎo)出組成模塊的公共 API 的聲明,就大功告成了。

除了導(dǎo)出,模塊內(nèi)的代碼只是非常簡單的正常代碼。它可以像 Object 和 Array 一樣使用全局變量。如果你的模塊在網(wǎng)絡(luò)瀏覽器中運行,它便可以使用文件和 XMLHttpRequest。

在一個單獨的文件中,我們可以導(dǎo)入和使用 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);
}

當(dāng)你運行一個包含導(dǎo)入聲明的模塊時,導(dǎo)入的模塊首先加載,然后每個模塊主體在一個深度優(yōu)先遍歷的依賴圖內(nèi)執(zhí)行,通過跳過已經(jīng)執(zhí)行的任何東西來避免循環(huán)。

這些都是模塊的基礎(chǔ)知識。這真的很簡單。;-)

導(dǎo)出列表

你可以寫一個單獨的列表來列出所有你想要導(dǎo)出的名字,外面加上大括號。

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

輸出列表并不一定是文件中的第一件事,它可以出現(xiàn)在一個模塊文件的頂層作用域內(nèi)的任何地方。你可以有多個導(dǎo)出列表,或?qū)?dǎo)出列表與其他導(dǎo)出聲明混合起來,只要沒有名字被導(dǎo)出超過一次。

重命名導(dǎo)入和導(dǎo)出

有時,導(dǎo)入的名字會和其他一些你同樣需要用的名字發(fā)生沖突。所以,ES6 可以讓你在導(dǎo)入它們的時候進行重命名。

// 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";
...

同樣,當(dāng)你導(dǎo)出他們的時候也可以進行重命名。如果在偶然情況下你想讓兩個不同的名字導(dǎo)出相同的值,這是非常方便的。

// 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
};

默認(rèn)導(dǎo)出

新標(biāo)準(zhǔn)旨在實現(xiàn)現(xiàn)有 CommonJS 和 AMD 模塊的互操作。因此,假設(shè)你有一個 Node 項目并且你已經(jīng)完成了 npm 安裝 lodash。你的 ES6 代碼可以從 Lodash 導(dǎo)入單個函數(shù)。

import {each, map} from "lodash";

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

但是也許你已經(jīng)習(xí)慣了看到_.each 而不是 each 并且你還是想用原來的方式編寫程序?;蛘吣阆胗?code>_作為一個函數(shù),因為它在 Lodash 中是有效的

對于這一點,你可以用一個稍微不同的語法:導(dǎo)入沒有花括號的模塊。

從“l(fā)odash”導(dǎo)入;

import _ from "lodash";

Shorthand 等同于從“l(fā)odash”導(dǎo)入{default as _}。所有的 CommonJS 和 AMD 模塊都呈現(xiàn)為 ES6 有一個 default 導(dǎo)出,如果你為該模塊調(diào)用了require() 你會得到同樣的東西——那便是導(dǎo)出對象。

ES6 可以讓你導(dǎo)出多件事情,但對于現(xiàn)有 CommonJS 模塊,默認(rèn)導(dǎo)出的就是你得到的。比如,截至本文發(fā)稿,著名的色彩包裝沒有任何的特別 ES6 支持。它是一個 CommonJS 模塊的集合,就像大部分 npm 的集合。但是你可以把它導(dǎo)入到你的 ES6 代碼。

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

如果你想讓你的 ES6 模塊有一個默認(rèn)的導(dǎo)出,那是非常容易的。默認(rèn)導(dǎo)出并沒有什么神奇的,它就像其它任何一個導(dǎo)出,除非它被命名為 “default”。就像我們之前談過的一樣,你可以使用重命名語法。

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

關(guān)鍵字導(dǎo)出默認(rèn)值后可以跟任何值:函數(shù),類,對象文本,無論什么。

模塊對象

很抱歉,這一期讓大家久等了。但是 JavaScript 并不孤單:出于某種原因,所有語言的模塊系統(tǒng)往往分別有很多又小又無聊的便利功能。幸運的是,只剩下一件事了。呃,兩件。

import * as cows from "cows";

從”cows”導(dǎo)入*作為cows; 當(dāng)你輸入*,導(dǎo)入的是一個模塊命名空間對象。它的屬性是模塊的導(dǎo)出。因此,如果”cows”模塊導(dǎo)出了一個名為moo()的函數(shù),然后用這種方式導(dǎo)入”cows”之后,你可以寫:cows.moo()

聚集模塊

有時,一個包的主模塊要多于導(dǎo)入所有包的其他模塊然后以統(tǒng)一方式導(dǎo)出它們。為了簡化這種代碼,還有一個集全功能于一身的導(dǎo)入與導(dǎo)出 shorthand:

// 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";

這些導(dǎo)出語句中的每一個都與一個后跟一導(dǎo)出的導(dǎo)入語句類似。不像真的導(dǎo)入,這沒有給你的作用域添加再導(dǎo)出綁定。所以如果你打算寫一些使用了 Tea 的 world-foods.js 代碼,不要用這個 shortland。你會發(fā)現(xiàn)它并不存在。

如果”singapore”導(dǎo)出的任何命名與其他導(dǎo)出發(fā)生了沖突,就會成為一個錯誤,所以謹(jǐn)慎使用 export*

呼!我們的語法部分完成了!到了有趣的部分。

import 實際上是用來做什么的?

你會相信…它什么都沒做嗎?

哦,你沒有那么輕易上當(dāng)。你相信該標(biāo)準(zhǔn)幾乎沒有說 import 是用來做什么的嗎?這會是一件好事嗎?

ES6 把模塊加載的細節(jié)全部交給了implementation。模塊執(zhí)行的其余部分都有詳述。

大致來說,當(dāng)你讓 JS 引擎運行一個模塊時,它會像在執(zhí)行這四個步驟:

1、解析:implementation 讀取模塊的源代碼,檢查語法錯誤。

2、加載:加載所有導(dǎo)入的模塊(遞歸)。這是還未標(biāo)準(zhǔn)化的部分。

3、連接:對于每個新加載的模塊,implementation 都會創(chuàng)建一個模塊作用域,并用該模塊中的所有綁定聲明填充,包括從其他模塊導(dǎo)入的東西。

這是當(dāng)你想從“paleo”導(dǎo)入{cake}的一部分,但是“paleo”并沒有真的導(dǎo)出任何名為 cake 的東西,你會得到一個錯誤。這太糟糕了,因為你與真正運行一些 JS 代碼近在咫尺,運行成功后就會得到 cake!

4、運行時間:最后,implementation 執(zhí)行各新加載模塊的語句。此時,導(dǎo)入處理已經(jīng)近似完成了,所以當(dāng)執(zhí)行到有一個導(dǎo)入聲明的一行代碼時…什么都沒發(fā)生!

看到了嗎?我說的是“什么都沒發(fā)生”。對于編程語言,我從不撒謊。

但是現(xiàn)在我們到了這個系統(tǒng)的有趣部分。有一個很酷的把戲。因為系統(tǒng)并沒有指定 loading 是如何工作的,而且你可以提前查看源代碼的導(dǎo)入聲明來找出所有的依賴,ES6 的 implementation 在編譯時免費做了所有的工作,它把你所有的模塊集成到一個單一文件里來把它們傳送到網(wǎng)絡(luò)上!像WebPACK一樣的工具實際上都是這樣做的。

這是一個大問題,因為在網(wǎng)頁上加載腳本需要時間,而且每當(dāng)你獲取一個,你可能會發(fā)現(xiàn)它包含著需要你加載幾十個的導(dǎo)入聲明。一個簡單的加載會需要很多的網(wǎng)絡(luò)往返。但是有了 WebPACK,你不僅可以在 ES6 中使用模塊,還會獲取到所有的軟件工程效益而沒有運行時的性能損失。

一個 ES6 模塊加載的詳細說明在初步準(zhǔn)備中——現(xiàn)在已經(jīng)完成了。它不是最終標(biāo)準(zhǔn)的一個原因是,在如何實現(xiàn)捆綁功能上沒有達成共識。我希望會有人可以解決它,因為就像我們會看到的,模塊加載真的應(yīng)該被標(biāo)準(zhǔn)化。捆綁放棄真是再好不過了。

靜態(tài)與動態(tài),或:規(guī)則,以及如何打破它們

對于動態(tài)語言,JavaScript 已經(jīng)有了一個令人驚訝的靜態(tài)模塊系統(tǒng)。

  • 所有的 import 和 export 都只允許存在于頂層模塊中。導(dǎo)入和導(dǎo)出沒有條件,但是你不能在函數(shù)作用域內(nèi)使用 import。

  • 所有導(dǎo)出的標(biāo)識必須在源代碼中有明確的命名。你不能以編程方式循環(huán)一個數(shù)組,也不能以數(shù)據(jù)驅(qū)動的方式導(dǎo)出一堆名字。

  • 模塊對象被凍結(jié)。我們無法以 polyfill 方式破解一個新功能到一個模塊對象。

  • 模塊的所有依賴必須在任何模塊代碼運行之前加載、解析和連接。于import而言,沒有可以延遲加載的語法。

  • 導(dǎo)入錯誤沒有錯誤恢復(fù)。一個應(yīng)用程序可能有數(shù)百個模塊,如果任何一個組成部分沒有加載或連接成功,整個程序都不會運行。你不能在一個 try/catch 塊中導(dǎo)入。(這里的好處是,因為系統(tǒng)是靜態(tài)的,所以 WebPACK 在你編譯的時候可以檢測到這些錯誤。)

  • 在依賴加載之前不允許模塊運行任何代碼。這代表著模塊無法控制他們的依賴是怎樣加載的。

只要你的需求是靜態(tài)的,系統(tǒng)是相當(dāng)不錯的。但是你可以想象一下有時你需要一個小黑客,對嗎?

那就是為什么你用的任何模塊加載系統(tǒng)都有一個編程 API,并依靠 ES6 的靜態(tài) import/export 語法。例如,WebPACK 包含了一個你可以用來進行“代碼分離”的API,在需求下緩慢加載一些模塊捆綁。相同的 API 可以幫助你打破上述的大部分其他規(guī)則。

ES6 模塊語法是非常靜態(tài)的,這樣非常好——它以提供強大的編譯工具的形式進行了回報。但是設(shè)計靜態(tài)語法是為了讓它輔助一個有著豐富動態(tài)的、程序化的裝載器 API。

什么時候可以使用 ES6 模塊?

如果現(xiàn)在你要使用模塊,你需要一個像TraceurBabel這樣的編譯器。在本系列的早些時候,加斯頓·席爾瓦展示了如何使用 Babel 和 Broccolito為網(wǎng)頁編譯 ES6 代碼。在那篇文章的基礎(chǔ)上,加斯頓有一個與 ES6 模塊相關(guān)的工作實例。阿克塞爾·勞施邁耶進行了講述,講述中還包括一個使用 Babel 和 WebPACK 的例子。

ES6 模塊系統(tǒng)主要由戴夫·赫爾曼和山姆·托賓·霍克施塔特設(shè)計,他們在多年的公開辯論中始終站在所有來者(包括我)的對立面為系統(tǒng)的靜態(tài)部分而辯護。喬恩·考皮爾德正在在火狐瀏覽器中推行模塊。JavaScript 程序標(biāo)準(zhǔn)的額外工作正在進行中。給 HTML 添加像 <script type=module之類東西的工作有望跟進。

這就是 ES6。

這是如此有趣,我不希望它結(jié)束。也許我們可以再做一集。我們可以討論在 ES6 說明中的還沒有足夠大到擁有單獨介紹文章的零碎的東西。也許談一點關(guān)于未來要把握的東西。下面,請聆聽 ES6 的出色結(jié)論。

上一篇:箭頭函數(shù)下一篇:生成器