[譯] 使用 `import()` 執行 JavaScript 代碼

使用 import() 執行 JavaScript 代碼

使用 import() 操做符,咱們能夠動態加載 ECMAScript 模塊。可是 import() 的應用不只於此,它還能夠做爲 eval() 的替代品,用來執行 JavaScript 代碼(這一點是最近 Andrea Giammarchi 向我指出的)。這篇博客將會解釋這是如何實現的。javascript

eval() 不支持 exportimport

eval() 的一大缺陷是:它不支持例如 exportimport 這樣的模塊語法。html

可是若是放棄 eval() 而改成使用 import(),咱們就能夠執行帶有模塊的代碼,在後文你將能看到這是如何實現的。前端

將來,咱們也許能夠使用 Realms,它也許會是可以支持模塊的、更強大的下一代 eval()java

使用 import() 執行簡單的代碼

下面,咱們從使用 import() 來執行 console.log() 開始學習:android

const js = `console.log('Hello everyone!');`;
const encodedJs = encodeURIComponent(js);
const dataUri = 'data:text/javascript;charset=utf-8,'
  + encodedJs;
import(dataUri);

// 輸出:
// 'Hello everyone!'
複製代碼

這段代碼執行後發生了什麼?ios

  • 首先,咱們建立了所謂的 數據 URI。這種類型的 URI 協議是 data:。URI 的剩餘部分中包含了全部資源的編碼,而不是指向資源自己的地址。這樣,數據 URI 就包含了一個完整的 ECMAScript 模塊 —— 它的 content 類型是 text/javascript
  • 而後咱們動態引入模塊,因而代碼被執行。

注意:這段代碼只能在瀏覽器中運行。在 Node.js 環境中,import() 不支持數據 URI。git

獲取被執行模塊的導出

import() 返回的 Promise 的完成態是一個模塊命名空間對象。這讓咱們能夠獲取到模塊的默認導出以及命名導出。在下面的例子中,咱們獲取得是默認導出:github

const js = `export default 'Returned value'`;
const dataUri = 'data:text/javascript;charset=utf-8,'
  + encodeURIComponent(js);
import(dataUri)
  .then((namespaceObject) => {
    assert.equal(namespaceObject.default, 'Returned value');
  });
複製代碼

使用標記模版建立數據 URI

使用一個適當的方法 esm(後文咱們會看到該方法是如何實現的),咱們能夠重寫上文的例子,並經過一個標記模版建立數據 URI:後端

const dataUri = esm`export default 'Returned value'`;
import(dataUri)
  .then((namespaceObject) => {
    assert.equal(namespaceObject.default, 'Returned value');
  });
複製代碼

esm 的實現以下:瀏覽器

function esm(templateStrings, ...substitutions) {
  let js = templateStrings.raw[0];
  for (let i=0; i<substitutions.length; i++) {
    js += substitutions[i] + templateStrings.raw[i+1];
  }
  return 'data:text/javascript;base64,' + btoa(js);
}
複製代碼

咱們把編碼方式從 charset=utf-8 切換爲 base64,它們二者的對好比下:

  • 源代碼:'a' < 'b'
  • 第一個數據 URI:data:text/javascript;charset=utf-8,'a'%20%3C%20'b'
  • 第二個數據 URI:data:text/javascript;base64,J2EnIDwgJ2In

每種編碼方式都各有利弊:

  • charset=utf-8(又稱百分號編碼)的優點:
    • 大部分源碼仍具備可讀性。
  • base64 的優點:
    • URI 更精短。
    • 更易嵌套(後文咱們會看到),由於它不包含任何如撇號這樣的特殊字符。

btoa() 是一個用來將字符串編碼爲 base 64 代碼的全局工具函數。注意:

  • 在 Node.js 環境下不可用。
  • 僅對碼點值在 0 至 255 範圍內的 Unicode 字符有效。

執行引用了其餘模塊的模塊

經過標記模版,咱們能夠嵌套數據 URI,並編碼引用了 m1 模塊的 m2 模塊:

const m1 = esm`export function f() { return 'Hello!' }`;
const m2 = esm`import {f} from '${m1}'; export default f()+f();`;
import(m2)
  .then(ns => assert.equal(ns.default, 'Hello!Hello!'));
複製代碼

擴展閱讀

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索