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