摘要:該模塊實現(xiàn)方案主要包含與這兩個關(guān)鍵字,其允許某個模塊對外暴露部分接口并且由其他模塊導(dǎo)入使用。由于在服務(wù)端的流行,的模塊形式被不正確地稱為。以上所描述的模塊載入機(jī)制均定義在中。
最近一直在搞基礎(chǔ)的東西,弄了一個持續(xù)更新的github筆記,可以去看看,誠意之作(本來就是寫給自己看的……)鏈接地址:Front-End-Basics
此篇文章的地址:JavaScript的模塊
基礎(chǔ)筆記的github地址:https://github.com/qiqihaobenben/Front-End-Basics ,可以watch,也可以star。
正文開始
JavaScript的模塊 介紹模塊通常是指編程語言所提供的代碼組織機(jī)制,利用此機(jī)制可將程序拆解為獨(dú)立且通用的代碼單元。所謂模塊化主要是解決代碼分割、作用域隔離、模塊之間的依賴管理以及發(fā)布到生產(chǎn)環(huán)境時的自動化打包與處理等多個方面。
模塊的優(yōu)點(diǎn)可維護(hù)性。 因為模塊是獨(dú)立的,一個設(shè)計良好的模塊會讓外面的代碼對自己的依賴越少越好,這樣自己就可以獨(dú)立去更新和改進(jìn)。
命名空間。 在 JavaScript 里面,如果一個變量在最頂級的函數(shù)之外聲明,它就直接變成全局可用。因此,常常不小心出現(xiàn)命名沖突的情況。使用模塊化開發(fā)來封裝變量,可以避免污染全局環(huán)境。
重用代碼。 我們有時候會喜歡從之前寫過的項目中拷貝代碼到新的項目,這沒有問題,但是更好的方法是,通過模塊引用的方式,來避免重復(fù)的代碼庫。
CommonJSCommonJS 最開始是 Mozilla 的工程師于 2009 年開始的一個項目,它的目的是讓瀏覽器之外的 JavaScript (比如服務(wù)器端或者桌面端)能夠通過模塊化的方式來開發(fā)和協(xié)作。
在 CommonJS 的規(guī)范中,每個 JavaScript 文件就是一個獨(dú)立的模塊上下文(module context),在這個上下文中默認(rèn)創(chuàng)建的屬性都是私有的。也就是說,在一個文件定義的變量(還包括函數(shù)和類),都是私有的,對其他文件是不可見的。
需要注意的是,CommonJS 規(guī)范的主要適用場景是服務(wù)器端編程,所以采用同步加載模塊的策略。如果我們依賴3個模塊,代碼會一個一個依次加載它們。
該模塊實現(xiàn)方案主要包含 require 與 module 這兩個關(guān)鍵字,其允許某個模塊對外暴露部分接口并且由其他模塊導(dǎo)入使用。
//sayModule.js function SayModule () { this.hello = function () { console.log("hello"); }; this.goodbye = function () { console.log("goodbye"); }; } module.exports = SayModule; //main.js 引入sayModule.js var Say = require("./sayModule.js"); var sayer = new Say(); sayer.hello(); //hello
作為一個服務(wù)器端的解決方案,CommonJS 需要一個兼容的腳本加載器作為前提條件。該腳本加載器必須支持名為 require 和 module.exports 的函數(shù),它們將模塊相互導(dǎo)入導(dǎo)出。
Node.jsNode 從 CommonJS 的一些創(chuàng)意中,創(chuàng)造出自己的模塊化實現(xiàn)。由于Node 在服務(wù)端的流行,Node 的模塊形式被(不正確地)稱為 CommonJS。
Node.js模塊可以分為兩大類,一類是核心模塊,另一類是文件模塊。
核心模塊 就是Node.js標(biāo)準(zhǔn)的API中提供的模塊,如fs、http、net等,這些都是由Node.js官方提供的模塊,編譯成了二進(jìn)制代碼,可以直接通過require獲取核心模塊,例如require("fs"),核心模塊擁有最高的加載優(yōu)先級,如果有模塊與核心模塊命名沖突,Node.js總是會加載核心模塊。
文件模塊 是存儲為多帶帶的文件(或文件夾)的模塊,可能是JavaScript代碼、JSON或編譯好的C/C++代碼。在不顯式指定文件模塊擴(kuò)展名的時候,Node.js會分別試圖加上.js、.json、.node(編譯好的C/C++代碼)。
加載方式
按路徑加載模塊
如果require參數(shù)以"/"開頭,那么就以絕對路徑的方式查找模塊名稱,如果參數(shù)以"./"、"../"開頭,那么則是以相對路徑的方式來查找模塊。
通過查找node_modules目錄加載模塊
如果require參數(shù)不以"/"、"./"、"../"開頭,而該模塊又不是核心模塊,那么就要通過查找node_modules加載模塊了。我們使用的npm獲取的包通常就是以這種方式加載的。
加載緩存
Node.js模塊不會被重復(fù)加載,這是因為Node.js通過文件名緩存所有加載過的文件模塊,所以以后再訪問到時就不會重新加載了。
注意: Node.js是根據(jù)實際文件名緩存的,而不是require()提供的參數(shù)緩存的,也就是說即使你分別通過require("express")和require("./node_modules/express")加載兩次,也不會重復(fù)加載,因為盡管兩次參數(shù)不同,解析到的文件卻是同一個。
Node.js 中的模塊在加載之后是以單例化運(yùn)行,并且遵循值傳遞原則:如果是一個對象,就相當(dāng)于這個對象的引用。
模塊載入過程
加載文件模塊的工作,主要由原生模塊module來實現(xiàn)和完成,該原生模塊在啟動時已經(jīng)被加載,進(jìn)程直接調(diào)用到runMain靜態(tài)方法。
例如運(yùn)行: node app.js Module.runMain = function () { // Load the main module--the command line argument. Module._load(process.argv[1], null, true); }; //_load靜態(tài)方法在分析文件名之后執(zhí)行 var module = new Module(id, parent); //并根據(jù)文件路徑緩存當(dāng)前模塊對象,該模塊實例對象則根據(jù)文件名加載。 module.load(filename);
具體說一下上文提到了文件模塊的三類模塊,這三類文件模塊以后綴來區(qū)分,Node.js會根據(jù)后綴名來決定加載方法,具體的加載方法在下文require.extensions中會介紹。
.js 通過fs模塊同步讀取js文件并編譯執(zhí)行。
.node 通過C/C++進(jìn)行編寫的Addon。通過dlopen方法進(jìn)行加載。
.json 讀取文件,調(diào)用JSON.parse解析加載。
接下來詳細(xì)描述js后綴的編譯過程。Node.js在編譯js文件的過程中實際完成的步驟有對js文件內(nèi)容進(jìn)行頭尾包裝。以app.js為例,包裝之后的app.js將會變成以下形式:
//circle.js var PI = Math.PI; exports.area = function (r) { return PI * r * r; }; exports.circumference = function (r) { return 2 * PI * r; }; //app.js var circle = require("./circle.js"); console.log( "The area of a circle of radius 4 is " + circle.area(4)); //app包裝后 (function (exports, require, module, __filename, __dirname) { var circle = require("./circle.js"); console.log("The area of a circle of radius 4 is " + circle.area(4)); }); //這段代碼會通過vm原生模塊的runInThisContext方法執(zhí)行(類似eval,只是具有明確上下文,不污染全局),返回為一個具體的function對象。最后傳入module對象的exports,require方法,module,文件名,目錄名作為實參并執(zhí)行。
這就是為什么require并沒有定義在app.js 文件中,但是這個方法卻存在的原因。從Node.js的API文檔中可以看到還有__filename、__dirname、module、exports幾個沒有定義但是卻存在的變量。其中__filename和__dirname在查找文件路徑的過程中分析得到后傳入的。module變量是這個模塊對象自身,exports是在module的構(gòu)造函數(shù)中初始化的一個空對象({},而不是null)。
在這個主文件中,可以通過require方法去引入其余的模塊。而其實這個require方法實際調(diào)用的就是module._load方法。
load方法在載入、編譯、緩存了module后,返回module的exports對象。這就是circle.js文件中只有定義在exports對象上的方法才能被外部調(diào)用的原因。
以上所描述的模塊載入機(jī)制均定義在lib/module.js中。
require 函數(shù)
require 引入的對象主要是函數(shù)。當(dāng) Node 調(diào)用 require() 函數(shù),并且傳遞一個文件路徑給它的時候,Node 會經(jīng)歷如下幾個步驟:
Resolving:找到文件的絕對路徑;
Loading:判斷文件內(nèi)容類型;
Wrapping:打包,給這個文件賦予一個私有作用范圍。這是使 require 和 module 模塊在本地引用的一種方法;
Evaluating:VM 對加載的代碼進(jìn)行處理的地方;
Caching:當(dāng)再次需要用這個文件的時候,不需要重復(fù)一遍上面步驟。
require.extensions 來查看對三種文件的支持情況
可以清晰地看到 Node 對每種擴(kuò)展名所使用的函數(shù)及其操作:對 .js 文件使用 module._compile;對 .json 文件使用 JSON.parse;對 .node 文件使用 process.dlopen。
文件查找策略
從文件模塊緩存中加載
盡管原生模塊與文件模塊的優(yōu)先級不同,但是優(yōu)先級最高的是從文件模塊的緩存中加載已經(jīng)存在的模塊。
從原生模塊加載
原生模塊的優(yōu)先級僅次于文件模塊緩存的優(yōu)先級。require方法在解析文件名之后,優(yōu)先檢查模塊是否在原生模塊列表中。以http模塊為例,盡管在目錄下存在一個http、http.js、http.node、http.json文件,require(“http”)都不會從這些文件中加載,而是從原生模塊中加載。
原生模塊也有一個緩存區(qū),同樣也是優(yōu)先從緩存區(qū)加載。如果緩存區(qū)沒有被加載過,則調(diào)用原生模塊的加載方式進(jìn)行加載和執(zhí)行。
從文件加載
當(dāng)文件模塊緩存中不存在,而且不是原生模塊的時候,Node.js會解析require方法傳入的參數(shù),并從文件系統(tǒng)中加載實際的文件,加載過程中的包裝和編譯細(xì)節(jié)在前面說過是調(diào)用load方法。
··
當(dāng) Node 遇到 require(X) 時,按下面的順序處理。 (1)如果 X 是內(nèi)置模塊(比如 require("http")) a. 返回該模塊。 b. 不再繼續(xù)執(zhí)行。 (2)如果 X 以 "./" 或者 "/" 或者 "../" 開頭 a. 根據(jù) X 所在的父模塊,確定 X 的絕對路徑。 b. 將 X 當(dāng)成文件,依次查找下面文件,只要其中有一個存在,就返回該文件,不再繼續(xù)執(zhí)行。 X X.js X.json X.node c. 將 X 當(dāng)成目錄,依次查找下面文件,只要其中有一個存在,就返回該文件,不再繼續(xù)執(zhí)行。 X/package.json(main字段) X/index.js X/index.json X/index.node (3)如果 X 不帶路徑 a. 根據(jù) X 所在的父模塊,確定 X 可能的安裝目錄。 b. 依次在每個目錄中,將 X 當(dāng)成文件名或目錄名加載。 (4) 拋出 "not found"
模塊循環(huán)依賴
//創(chuàng)建兩個文件,module1.js 和 module2.js,并且讓它們相互引用 // module1.js exports.a = 1; require("./module2"); exports.b = 2; exports.c = 3; // module2.js const Module1 = require("./module1"); console.log("Module1 is partially loaded here", Module1); //執(zhí)行 node module2.js 打印:Module1 is partially loaded here {a:1,b:2,c:3} //執(zhí)行 node module1.js 打印:Module1 is partially loaded here {a:1}
在 module1 完全加載之前需要先加載 module2,而 module2 的加載又需要 module1。這種狀態(tài)下,我們從 exports 對象中能得到的就是在發(fā)生循環(huán)依賴之前的這部分。上面代碼中,只有 a 屬性被引入,因為 b 和 c 都需要在引入 module2 之后才能加載進(jìn)來。
Node 使這個問題簡單化,在一個模塊加載期間開始創(chuàng)建 exports 對象。如果它需要引入其他模塊,并且有循環(huán)依賴,那么只能部分引入,也就是只能引入發(fā)生循環(huán)依賴之前所定義的這部分。
AMDAMD 是 Asynchronous Module Definition 的簡稱,即“異步模塊定義”,是從 CommonJS 討論中誕生的。AMD 優(yōu)先照顧瀏覽器的模塊加載場景,使用了異步加載和回調(diào)的方式。
AMD 和 CommonJS 一樣需要腳本加載器,盡管 AMD 只需要對 define 方法的支持。define 方法需要三個參數(shù):模塊名稱,模塊運(yùn)行的依賴數(shù)組,所有依賴都可用之后執(zhí)行的函數(shù)(該函數(shù)按照依賴聲明的順序,接收依賴作為參數(shù))。只有函數(shù)參數(shù)是必須的。define 既是一種引用模塊的方式,也是定義模塊的方式。
// file lib/sayModule.js define(function (){ return { sayHello: function () { console.log("hello"); } }; }); //file main.js define(["./lib/sayModule"], function (say){ say.sayHello(); //hello })
main.js 作為整個應(yīng)用的入口模塊,我們使用 define 關(guān)鍵字聲明了該模塊以及外部依賴(沒有生命模塊名稱);當(dāng)我們執(zhí)行該模塊代碼時,也就是執(zhí)行 define 函數(shù)的第二個參數(shù)中定義的函數(shù)功能,其會在框架將所有的其他依賴模塊加載完畢后被執(zhí)行。這種延遲代碼執(zhí)行的技術(shù)也就保證了依賴的并發(fā)加載。
RequireJSRequireJS 是一個前端的模塊化管理的工具庫,遵循AMD規(guī)范,通過一個函數(shù)來將所有所需要的或者說所依賴的模塊實現(xiàn)裝載進(jìn)來,然后返回一個新的函數(shù)(模塊),我們所有的關(guān)于新模塊的業(yè)務(wù)代碼都在這個函數(shù)內(nèi)部操作,其內(nèi)部也可無限制的使用已經(jīng)加載進(jìn)來的以來的模塊。
//scripts下的main.js則是指定的主代碼腳本文件,所有的依賴模塊代碼文件都將從該文件開始異步加載進(jìn)入執(zhí)行。
defined用于定義模塊,RequireJS要求每個模塊均放在獨(dú)立的文件之中。按照是否有依賴其他模塊的情況分為獨(dú)立模塊和非獨(dú)立模塊。
1、獨(dú)立模塊 不依賴其他模塊。直接定義
define({ methodOne: function (){}, methodTwo: function (){} }); //等價于 define(function (){ return { methodOne: function (){}, methodTwo: function (){} }; });
2、非獨(dú)立模塊,對其他模塊有依賴
define([ "moduleOne", "moduleTwo" ], function(mOne, mTwo){ ... }); //或者 define( function( require ){ var mOne = require( "moduleOne" ), mTwo = require( "moduleTwo" ); ... });
如上代碼, define中有依賴模塊數(shù)組的 和 沒有依賴模塊數(shù)組用require加載 這兩種定義模塊,調(diào)用模塊的方法合稱為AMD模式,定義模塊清晰,不會污染全局變量,清楚的顯示依賴關(guān)系。AMD模式可以用于瀏覽器環(huán)境并且允許非同步加載模塊,也可以按需動態(tài)加載模塊。
CMDCMD(Common Module Definition),在CMD中,一個模塊就是一個文件。
全局函數(shù)define,用來定義模塊。
參數(shù) factory 可以是一個函數(shù),也可以為對象或者字符串。
當(dāng) factory 為對象、字符串時,表示模塊的接口就是該對象、字符串。
定義JSON數(shù)據(jù)模塊:
define({ "foo": "bar" });
factory 為函數(shù)的時候,表示模塊的構(gòu)造方法,執(zhí)行構(gòu)造方法便可以得到模塊向外提供的接口。
define( function(require, exports, module) { // 模塊代碼 });SeaJS
sea.js 核心特征:
遵循CMD規(guī)范,與NodeJS般的書寫模塊代碼。
依賴自動加載,配置清晰簡潔。
seajs.use用來在頁面中加載一個或者多個模塊
// 加載一個模塊 seajs.use("./a"); // 加載模塊,加載完成時執(zhí)行回調(diào) seajs.use("./a",function(a){ a.doSomething(); }); // 加載多個模塊執(zhí)行回調(diào) seajs.use(["./a","./b"],function(a , b){ a.doSomething(); b.doSomething(); });
AMD和CMD最大的區(qū)別是對依賴模塊的執(zhí)行時機(jī)處理不同,注意不是加載的時機(jī)或者方式不同。UMD
很多人說requireJS是異步加載模塊,SeaJS是同步加載模塊,這么理解實際上是不準(zhǔn)確的,其實加載模塊都是異步的,只不過AMD依賴前置,js可以方便知道依賴模塊是誰,立即加載,而CMD就近依賴,需要使用把模塊變?yōu)樽址馕鲆槐椴胖酪蕾嚵四切┠K,這也是很多人詬病CMD的一點(diǎn),犧牲性能來帶來開發(fā)的便利性,實際上解析模塊用的時間短到可以忽略。為什么說是執(zhí)行時機(jī)處理不同?
同樣都是異步加載模塊,AMD在加載模塊完成后就會執(zhí)行該模塊,所有模塊都加載執(zhí)行完后會進(jìn)入回調(diào)函數(shù),執(zhí)行主邏輯,這樣的效果就是依賴模塊的執(zhí)行順序和書寫順序不一定一致,看網(wǎng)絡(luò)速度,哪個先下載下來,哪個先執(zhí)行,但是主邏輯一定在所有依賴加載完成后才執(zhí)行。
CMD加載完某個依賴模塊后并不執(zhí)行,只是下載而已,在所有依賴模塊加載完成后進(jìn)入主邏輯,遇到require語句的時候才執(zhí)行對應(yīng)的模塊,這樣模塊的執(zhí)行順序和書寫順序是完全一致的。
統(tǒng)一模塊定義(UMD:Universal Module Definition )就是將 AMD 和 CommonJS 合在一起的一種嘗試,常見的做法是將CommonJS 語法包裹在兼容 AMD 的代碼中。
(function(define) { define(function () { return { sayHello: function () { console.log("hello"); } }; }); }( typeof module === "object" && module.exports && typeof define !== "function" ? function (factory) { module.exports = factory(); } : define ));
該模式的核心思想在于所謂的 IIFE(Immediately Invoked Function Expression),該函數(shù)會根據(jù)環(huán)境來判斷需要的參數(shù)類別
ES6模塊(module) 嚴(yán)格模式?ES6 的模塊自動采用嚴(yán)格模式,不管有沒有在模塊頭部加上"use strict";。
嚴(yán)格模式主要有以下限制。
變量必須聲明后再使用
函數(shù)的參數(shù)不能有同名屬性,否則報錯
不能使用with語句
不能對只讀屬性賦值,否則報錯
不能使用前綴0表示八進(jìn)制數(shù),否則報錯
不能刪除不可刪除的屬性,否則報錯
不能刪除變量delete prop,會報錯,只能刪除屬性delete global[prop]
eval不會在它的外層作用域引入變量
eval和arguments不能被重新賦值
arguments不會自動反映函數(shù)參數(shù)的變化
不能使用arguments.callee
不能使用arguments.caller
禁止this指向全局對象
不能使用fn.caller和fn.arguments獲取函數(shù)調(diào)用的堆棧
增加了保留字(比如protected、static和interface)
模塊Module一個模塊,就是一個對其他模塊暴露自己的屬性或者方法的文件。
導(dǎo)出Export作為一個模塊,它可以選擇性地給其他模塊暴露(提供)自己的屬性和方法,供其他模塊使用。
// profile.js export var firstName = "qiqi"; export var lastName = "haobenben"; export var year = 1992; //等價于 var firstName = "qiqi"; var lastName = "haobenben"; var year = 1992; export {firstName, lastName, year}
1、 通常情況下,export輸出的變量就是本來的名字,但是可以使用as關(guān)鍵字重命名。
function v1() { ... } function v2() { ... } export { v1 as streamV1, v2 as streamV2, v2 as streamLatestVersion }; //上面代碼使用as關(guān)鍵字,重命名了函數(shù)v1和v2的對外接口。重命名后,v2可以用不同的名字輸出兩次。
2、 需要特別注意的是,export命令規(guī)定的是對外的接口,必須與模塊內(nèi)部的變量建立一一對應(yīng)關(guān)系。
// 報錯 export 1; // 報錯 var m = 1; export m; //上面兩種寫法都會報錯,因為沒有提供對外的接口。第一種寫法直接輸出1,第二種寫法通過變量m,還是直接輸出1。1只是一個值,不是接口。 / 寫法一 export var m = 1; // 寫法二 var m = 1; export {m}; // 寫法三 var n = 1; export {n as m}; //上面三種寫法都是正確的,規(guī)定了對外的接口m。其他腳本可以通過這個接口,取到值1。它們的實質(zhì)是,在接口名與模塊內(nèi)部變量之間,建立了一一對應(yīng)的關(guān)系。
3、最后,export命令可以出現(xiàn)在模塊的任何位置,只要處于模塊頂層就可以。如果處于塊級作用域內(nèi),就會報錯,接下來說的import命令也是如此。
function foo() { export default "bar" // SyntaxError } foo()導(dǎo)入import
作為一個模塊,可以根據(jù)需要,引入其他模塊的提供的屬性或者方法,供自己模塊使用。
1、 import命令接受一對大括號,里面指定要從其他模塊導(dǎo)入的變量名。大括號里面的變量名,必須與被導(dǎo)入模塊(profile.js)對外接口的名稱相同。如果想為輸入的變量重新取一個名字,import命令要使用as關(guān)鍵字,將輸入的變量重命名。
import { lastName as surename } from "./profile";
2、import后面的from指定模塊文件的位置,可以是相對路徑,也可以是絕對路徑,.js路徑可以省略。如果只是模塊名,不帶有路徑,那么必須有配置文件,告訴 JavaScript 引擎該模塊的位置。
3、注意,import命令具有提升效果,會提升到整個模塊的頭部,首先執(zhí)行。
foo(); import { foo } from "my_module"; //上面的代碼不會報錯,因為import的執(zhí)行早于foo的調(diào)用。這種行為的本質(zhì)是,import命令是編譯階段執(zhí)行的,在代碼運(yùn)行之前。
4、由于import是靜態(tài)執(zhí)行,所以不能使用表達(dá)式和變量,這些只有在運(yùn)行時才能得到結(jié)果的語法結(jié)構(gòu)。
/ 報錯 import { "f" + "oo" } from "my_module"; // 報錯 let module = "my_module"; import { foo } from module; // 報錯 if (x === 1) { import { foo } from "module1"; } else { import { foo } from "module2"; }
5、最后,import語句會執(zhí)行所加載的模塊,因此可以有下面的寫法。
import "lodash"; //上面代碼僅僅執(zhí)行l(wèi)odash模塊,但是不輸入任何值。默認(rèn)導(dǎo)出(export default)
每個模塊支持我們導(dǎo)出一個沒有名字的變量,使用關(guān)鍵語句export default來實現(xiàn).
export default function(){ ?? ? ?? console.log("I am default Fn"); ? ?} //使用export default關(guān)鍵字對外導(dǎo)出一個匿名函數(shù),導(dǎo)入這個模塊的時候,可以為這個匿名函數(shù)取任意的名字 //取任意名字均可 import sayDefault from "./module-B.js"; sayDefault(); //結(jié)果:I am default Fn
1、默認(rèn)輸出和正常輸出的比較
// 第一組 export default function diff() { // 輸出 // ... } import diff from "diff"; // 輸入 // 第二組 export function diff() { // 輸出 // ... }; import {diff} from "diff"; // 輸入 //上面代碼的兩組寫法,第一組是使用export default時,對應(yīng)的import語句不需要使用大括號;第二組是不使用export default時,對應(yīng)的import語句需要使用大括號。
export default命令用于指定模塊的默認(rèn)輸出。顯然,一個模塊只能有一個默認(rèn)輸出,因此export default命令只能使用一次。所以,import命令后面才不用加大括號,因為只可能對應(yīng)一個方法。
2、因為export default本質(zhì)是將該命令后面的值,賦給default變量以后再默認(rèn),所以直接將一個值寫在export default之后。
/ 正確 export default 42; // 報錯 export 42; //上面代碼中,后一句報錯是因為沒有指定對外的接口,而前一句指定外對接口為default。
3、如果想在一條import語句中,同時輸入默認(rèn)方法和其他變量,可以寫成下面這樣。
import _, { each } from "lodash"; //對應(yīng)上面代碼的export語句如下 export default function (){ //... } export function each (obj, iterator, context){ //... }export 與 import 的復(fù)合寫法
如果在一個模塊之中,先輸入后輸出同一個模塊,import語句可以與export語句寫在一起。
export { foo, bar } from "my_module"; // 等同于 import { foo, bar } from "my_module"; export { foo, bar }; / 接口改名 export { foo as myFoo } from "my_module"; // 整體輸出 export * from "my_module";
注意事項ES6 中的循環(huán)引用
1、聲明的變量,對外都是只讀的。但是導(dǎo)出的是對象類型的值,就可修改。
2、導(dǎo)入不存在的變量,值為undefined。
ES6 中,imports 是 exports 的只讀視圖,直白一點(diǎn)就是,imports 都指向 exports 原本的數(shù)據(jù),比如:
//------ lib.js ------ export let counter = 3; export function incCounter() { counter++; } //------ main.js ------ import { counter, incCounter } from "./lib"; // The imported value `counter` is live console.log(counter); // 3 incCounter(); console.log(counter); // 4 // The imported value can’t be changed counter++; // TypeError
因此在 ES6 中處理循環(huán)引用特別簡單,看下面這段代碼:
//------ a.js ------ import {bar} from "b"; // (1) export function foo() { bar(); // (2) } //------ b.js ------ import {foo} from "a"; // (3) export function bar() { if (Math.random()) { foo(); // (4) } }
假設(shè)先加載模塊 a,在模塊 a 加載完成之后,bar 間接性地指向的是模塊 b 中的 bar。無論是加載完成的 imports 還是未完成的 imports,imports 和 exports 之間都有一個間接的聯(lián)系,所以總是可以正常工作。
實例//---module-B.js文件--- //導(dǎo)出變量:name export var name = "cfangxu"; moduleA模塊代碼: //導(dǎo)入 模塊B的屬性 name ? ? import { name } from "./module-B.js";? ? console.log(name) //打印結(jié)果:cfangxu
批量導(dǎo)出
//屬性name var name = "cfangxu"; //屬性age var age? = 26; //方法 say var say = function(){ ? ???? console.log("say hello"); ? ?} //批量導(dǎo)出 export {name,age,say}
批量導(dǎo)入
//導(dǎo)入 模塊B的屬性 import { name,age,say } from "./module-B.js"; console.log(name) //打印結(jié)果:cfangxu console.log(age) //打印結(jié)果:26 say() //打印結(jié)果:say hello
重命名導(dǎo)入變量
import {name as myName} from "./module-B.js"; console.log(myName) //cfangxu
整體導(dǎo)入
/使用*實現(xiàn)整體導(dǎo)入 import * as obj from "./module-B.js"; console.log(obj.name) //結(jié)果:"cfangxu" console.log(obj.age) //結(jié)果:26 obj.say(); //結(jié)果:say hello推薦資料
JavaSript模塊規(guī)范 - AMD規(guī)范與CMD規(guī)范介紹
JavaScript 模塊演化簡史
require() 源碼解讀
在 Node.js 中引入模塊:你所需要知道的一切都在這里
深入淺出Node.js(三):深入Node.js的模塊機(jī)制
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/92210.html
摘要:正文開始三種本地存儲方式前言網(wǎng)絡(luò)早期最大的問題之一是如何管理狀態(tài)。這個特點(diǎn)很重要,因為這關(guān)系到什么樣的數(shù)據(jù)適合存儲在中。特點(diǎn)生命周期持久化的本地存儲,除非主動刪除數(shù)據(jù),否則數(shù)據(jù)是永遠(yuǎn)不會過期的。 最近一直在搞基礎(chǔ)的東西,弄了一個持續(xù)更新的github筆記,可以去看看,誠意之作(本來就是寫給自己看的……)鏈接地址:Front-End-Basics 此篇文章的地址:三種本地存儲方式 ...
摘要:我的書簽我的書簽謹(jǐn)慎導(dǎo)入,小心覆蓋工具類版本管理快速切換源配置教程指南可視化工具前端工具集前端助手網(wǎng)絡(luò)封包截取工具格式化工具標(biāo)注工具模擬請求類深入淺出布局你所不知道的動畫技巧與細(xì)節(jié)常用代碼黑魔法小技巧,讓你少寫不必要的,代碼更優(yōu)雅一勞永 我的書簽 我的書簽(謹(jǐn)慎導(dǎo)入,小心覆蓋) 工具類 nvm: node版本管理 nrm: 快速切換npm源 shell: zsh+on-my-zsh配...
摘要:我的書簽我的書簽謹(jǐn)慎導(dǎo)入,小心覆蓋工具類版本管理快速切換源配置教程指南可視化工具前端工具集前端助手網(wǎng)絡(luò)封包截取工具格式化工具標(biāo)注工具模擬請求類深入淺出布局你所不知道的動畫技巧與細(xì)節(jié)常用代碼黑魔法小技巧,讓你少寫不必要的,代碼更優(yōu)雅一勞永 我的書簽 我的書簽(謹(jǐn)慎導(dǎo)入,小心覆蓋) 工具類 nvm: node版本管理 nrm: 快速切換npm源 shell: zsh+on-my-zsh配...
摘要:小弟在前端摸爬滾打一段時間,發(fā)現(xiàn)前端的比較好的文檔比較分散,特別是中文的,我平時都是收藏在瀏覽器里,以后有好的教程和綜合性的文檔我會更新到這里。小組中文文檔,很全。 小弟在前端摸爬滾打一段時間,發(fā)現(xiàn)前端的比較好的文檔比較分散,特別是中文的,我平時都是ctrl+D收藏在瀏覽器里,以后有好的教程和綜合性的文檔我會更新到這里。一則可以做個記錄,防止丟失。二則有需要的朋友可以來我這里找一找。 ...
閱讀 3069·2021-11-24 11:14
閱讀 3477·2021-11-22 15:22
閱讀 3199·2021-09-27 13:36
閱讀 710·2021-08-31 14:29
閱讀 1327·2019-08-30 15:55
閱讀 1751·2019-08-29 17:29
閱讀 1142·2019-08-29 16:24
閱讀 2399·2019-08-26 13:48