摘要:模塊可以導入和導出各種類型的變量,如函數,對象,字符串,數字,布爾值,等等。所以這可能會導致一些不符合預期的行為。可變的基本類型值在導入一些基本類型的值如數字,布爾值或字符串時,可能會產生一個有趣的副作用。
前言
ECMAScript 2015(又稱ES6)提供了一個前端JavaScript缺失已久的特性 —— 模塊。ES2015中的模塊參考了CommonJS規范(目前Node.js的模塊規范)以及AMD規范,并且盡可能的取其精華,去其糟粕:
它提供了簡潔的語法
以及異步的,可配置的模塊加載
這篇文章將會專注于ES2015的模塊語法以及注意點。關于模塊的加載和打包,將會在另一篇文章中細述。
為什么要使用模塊?目前最普遍的JavaScript運行平臺便是瀏覽器,在瀏覽器中,所有的代碼都運行在同一個全局上下文中。這使得你即使更改應用中的很小一部分,你也要擔心可能會產生的命名沖突。
傳統的JavaScript應用被分離在多個文件中,并且在構建的時候連接在一起,這稍顯笨重。所以人們開始將每個文件內的代碼都包在一個自執行函數中:(function() { ... })();。這種方法創建了一個本地作用域,于是最初的模塊化的概念產生了。之后的CommonJS和AMD系統中所稱的模塊,也是由此實現的。
換句話說,現存的“模塊”系統是使用已有的語言特性所實現的。而ES2015則通過添加適當的新的語言特性,來使之官方化了。
創建模塊一個JavaScript模塊就是一個對其他模塊暴露一些內部屬性/方法的文件。我們在這里僅會討論瀏覽器中的ES2015模塊系統,并不會涉及Node.js是如何組織它自身的模塊的。一些在創建ES2015模塊時需要注意的點:
每個模塊都有自己的上下文和傳統的JavaScript不同,在使用模塊時,你不必擔心污染全局作用域。恰恰相反,你需要把所以你需要用到的東西從其他模塊中導入進來。但是,這樣也會使模塊之間的依賴關系更為清晰。
模塊的名字模塊的名字由它的文件名或文件夾名所決定,并且你可以忽略它的.js后綴:
如果你有一個叫utils.js的文件,那么你可以通過./utils這樣的相對路徑導入它
如果你有一個叫./utils/index.js的文件,則你可以通過./utils/index或./utils來導入它。這使得你可以批量導入一個文件夾內的所有模塊。
導出和導入可以使用ES2015的新關鍵字import和exports來導入或導出模塊中的東西。模塊可以導入和導出各種類型的變量,如函數,對象,字符串,數字,布爾值,等等。
默認導出每一個模塊都支持導出一個不具名的變量,這稱作默認導出:
// hello-world.js export default function() {} // main.js import helloWorld from "./hello-world"; import anotherFunction from "./hello-world"; helloWorld(); console.log(helloWorld === anotherFunction);
等價的CommonJS語法:
// hello.js module.exports = function() {} // main.js var helloWorld = require("./hello-world"); var anotherFunction = require("./hello-world"); helloWorld(); console.log(helloWorld === anotherFunction);
任何的JavaScript值都可以被默認導出:
export default 3.14; export default {foo: "bar"}; export default "hello world";具名導出
除了默認導出外,ES2015的模塊系統還支持導出任意數量個具名的變量:
const PI = 3.14; const value = 42; export function helloWorld() {} export {PI, value};
等價的CommonJS語法:
var PI = 3.14; var value = 42; module.exports.helloWorld = function() {} module.exports.PI = PI; module.exports.value = value;
你也可以在導出變量時對其重命名:
const value = 42; export {value as THE_ANSWER};
等價的CommonJS語法:
var value = 42; module.exports.THE_ANSWER = value;
在導入時,你也可以使用as關鍵字來重命名導入的變量:
import {value as THE_ANSWER} from "./module";
等價的CommonJS語法:
var THE_ANSWER = require("./module"").value;導入所有
最簡單的,在一條命令中導入一個模塊中所有變量的方法,是使用*標記。這樣一來,被導入模塊中所有導出的變量都會變成它的屬性,默認導出的變量則會被置于default屬性中。
// module.js export default 3.14; export const table = {foo: "bar"}; export function hello() {}; // main.js import * as module from "./module"; console.log(module.default); console.log(module.table); console.log(module.hello());
等價的CommonJS語法:
// module.js module.exports.default = 3.14; module.exports.table = {foo: "bar"}; module.exports.hello = function () {}; // main.js var module = require("./module"); console.log(module.default); console.log(module.table); console.log(module.hello());
值得再強調的是,import * as foo from和import foo from的區別。后者僅僅會導入默認導出的變量,而前者則會在一個對象中導入所有。
導出所有一個可能的需求是,你需要將另一個模塊中的一些(或所有)值在你的模塊中再次導出,這被稱作二次導出(re-exporting)。值得注意的是,你可以二次導出許多同名的值,這將不會導致異常,而是最后一個被導出的值將會獲得勝利。
// module.js const PI = 3.14; const value = 42; export const table = {foo: "bar"}; export function hello() {}; // main.js export * from "./module"; export {hello} from "./module"; export {hello as foo} from "./module";
等價的CommonJS語法:
// module.js module.exports.table = {foo: "bar"}; module.exports.hello = function () {}; // main.js module.exports = require("./module"); module.exports.hello = require("./module").hello; module.exports.foo = require("./module").hello;注意點
一個關鍵點時,導入模塊的東西,并不是一個引用或一個值,而是一個類似與被導入模塊內部的一個getter對象。所以這可能會導致一些不符合預期的行為。
缺乏異常在具名地導入其他模塊的變量時,如果你不小心打錯了變量名,這將不會拋出異常,而是導入的值將會變成undefined。
// module.js export const value = 42; // main.js import {valu} from "./module"; // no errors console.log(valu); // undefined可變的基本類型值
在導入一些基本類型的值(如數字,布爾值或字符串)時,可能會產生一個有趣的副作用。這些值可能會在模塊外被修改。例子:
// module.js export let count = 0; export function inc() { count++; } // main.js import {count, inc} from "./module"; // `count` is a `Number` variable assert.equal(count, 0); inc(); assert.equal(count, 1);
上面的例子中,count變量是一個數值類型,它在main模塊中被修改了值。
導入的變量是只讀的不論你以何種聲明導出變量,它們都是只讀的。但是,如果導出的是對象,你可以改變變量的屬性。
// module.js export let count = 0; export const table = {foo: "bar"}; // main.js import {count, table} from "./module; table.foo = "Bar"; // OK count++; // read-only error測試模塊
如果想要測試,或mock被導出的變量,很不幸,這在新的ES2015模塊系統中是辦不到的。因為與CommonJS一樣,導出的變量在外面不能被重新賦值。唯一的解決辦法是,導出一個多帶帶的對象。
// module.js export default { value: 42, print: () => console.log(this.value) } // module-test.js import m from "./module"; m.value = 10; m.print(); // 10最后
ES2015的模塊標準化了模塊的加載和解析方式。CommonJS和AMD之間的爭論終于被解決了。
我們得到了更簡潔的模塊語法,以及靜態的模塊定義,這有助于編譯器的優化,甚至是類型檢查。
原文鏈接:https://strongloop.com/strongblog/an-introduction-to-javascript-es6-modules/
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78256.html
摘要:解構賦值允許我們將右邊的表達式看起來也像變量聲明一般,然后在左邊將值一一提取。數組的解構賦值現在假設我們有一個變量,其值為。通過,這會看上去更清晰簡潔最后的解構賦值給的語法帶來了更多的現代化。 前言 讓我們來仔細地看看ES6所帶來的更清晰的變量聲明與賦值語法。現今的變量聲明語法十分的直接:左邊是一個變量名,右邊可以是一個數組:[]的表達式或一個對象:{}的表達式,等等。解構賦值允許我...
摘要:前言又稱通過一些新的關鍵字,使類成為了中一個新的一等公民。類聲明在中,有兩個聲明類的方式。在使用了新的關鍵字后在底層,所做的,也只是將這個方法添加為構造函數的一個屬性。在想要調用父類的構造函數時,你可以簡單地將關鍵字視作一個函數使用,如。 前言 EcmaScript 2015 (又稱ES6)通過一些新的關鍵字,使類成為了JS中一個新的一等公民。但是目前為止,這些關于類的新關鍵字僅僅是建...
摘要:前言又稱提供一個全新的迭代器的概念,它允許我們在語言層面上定義一個有限或無限的序列。后者可以被用來幫助我們理解迭代器。但是當我們使用迭代器時,這個問題就迎刃而解了。是中的新語法,用來配合迭代器。這是因為數組的迭代器只返回其中預期的元素。 前言 EcmaScript 2015 (又稱ES6)提供一個全新的迭代器的概念,它允許我們在語言層面上定義一個(有限或無限的)序列。 暫時先拋開它...
摘要:以下例子的目的是使用來展示一個每秒都會更新的時鐘當嘗試在的回調中使用來引用元素時,很不幸,我們得到的只是一個屬于回調函數自身上下文的。 前言 胖箭頭函數(Fat arrow functions),又稱箭頭函數,是一個來自ECMAScript 2015(又稱ES6)的全新特性。有傳聞說,箭頭函數的語法=>,是受到了CoffeeScript 的影響,并且它與CoffeeScript中的=>...
摘要:如果你的運行緩慢,你可以考慮是否能優化請求,減少對的操作,盡量少的操,或者犧牲其它的來換取性能。在認識描述這些核心元素的過程中,我們也會分享一些當我們構建的時候遵守的一些經驗規則,一個應用應該保持健壯和高性能來維持競爭力。 一個開源的前端錯誤收集工具 frontend-tracker,你值得收藏~ 蒲公英團隊最近開發了一款前端錯誤收集工具,名叫 frontend-tracker ,這款...
閱讀 688·2021-11-18 10:07
閱讀 2878·2021-09-22 16:04
閱讀 873·2021-08-16 10:50
閱讀 3326·2019-08-30 15:56
閱讀 1784·2019-08-29 13:22
閱讀 2647·2019-08-26 17:15
閱讀 1229·2019-08-26 10:57
閱讀 1103·2019-08-23 15:23