es6模塊

模塊化主要是幫助我們更好的組織代碼林束, 模塊允許我們將相關(guān)的變量和函數(shù)放在一個模塊中。 在ES6模塊化之前稽亏,JS語言并沒有模塊的概念壶冒,只有函數(shù)作用域和全局作用域非常容易發(fā)生命名沖突。 之前的RequireJS, SeaJS, AMD, UMD,CMD在一定層面上都是為了解決JS模塊化的問題截歉。ES6模塊取其精華:

  • 它提供了簡潔的語法
  • 以及異步的胖腾, 可配置的模塊加載

什么是模塊

模塊是自動運行在嚴格模式下并且沒有辦法退出運行的JavaScript代碼

  1. 在模塊的頂部this的值是undefined
    2.其模塊不支持html風(fēng)格的代碼注釋
  2. 除非用default關(guān)鍵字,否則不能用這個語法導(dǎo)出匿名函數(shù)或類

任何未限制導(dǎo)出的變量怎披、函數(shù)或類都是模塊私有的胸嘁,無法從模塊外部訪問

為什么要使用模塊

目前最普遍的JS運行平臺便是瀏覽器,在瀏覽器中凉逛,所有的代碼都運行在同一個全局上下文中性宏, 這使得你即使更改應(yīng)用中的很小一部分, 你也要擔(dān)心可能會產(chǎn)生的命名沖突状飞。

傳統(tǒng)的JS應(yīng)用被分離在多個文件夾中毫胜,并且在構(gòu)建的時候連接在一起书斜,這稍顯笨重。所以人們開始將每個文件內(nèi)的代碼都包在一個自執(zhí)行函數(shù)中: (function(){ ... })(); 酵使。 這種方法創(chuàng)建了一個本地作用域荐吉,于是最初的模塊化的概念產(chǎn)生了, 之后的CommonJS和AMD系統(tǒng)中所稱的模塊口渔, 也是由此實現(xiàn)的样屠。

創(chuàng)建模塊

一個JS模塊就是一個對其他模塊暴露一些內(nèi)部的屬性、方法的文件缺脉。 這里僅討論瀏覽器中的ES2016模塊系統(tǒng)痪欲。

每個模塊都有自己的上下文

和傳統(tǒng)的JS不同,在使用模塊時攻礼,你不必擔(dān)心污染全局作用域业踢。恰恰相反,你需要把所有你需要用到的東西從其他模塊中導(dǎo)入進來礁扮,這樣會使得模塊之間的依賴關(guān)系更為清晰

導(dǎo)入導(dǎo)出

可以使用ES6的新關(guān)鍵字 importexports 來導(dǎo)入或?qū)С瞿K中的對象知举。 模塊可以導(dǎo)入和導(dǎo)出各種類型的變量,如函數(shù)太伊,對象雇锡,字符串,數(shù)字倦畅,布爾值等遮糖。

默認導(dǎo)出

每一個模塊都支持導(dǎo)出一個不具名的變量,這為默認導(dǎo)出:

// helloWord.js
export default function(){
    console.log('111');
}

// main.js
import hello from './helloWord';
import anotherHello from './helloWord';

hello(); // 111
anotherHello(); //111
console.log(hello === anotherHello); //true

等價的CommonJS語法:

// helloWord.js
module.exports = function(){
    console.log('111');
}

//main.js
var hello = require('./helloWord');
var anotherHello = require('./helloWord');

hello(); // 111
anotherHello(); //111
console.log(hello === anotherHello); //true

任何JS值都是可以被默認導(dǎo)出的:

// helloWord.js
export default 3.14
//export default { foo: 'bar' };
//export default 'hello word';

// main.js
import hello from './helloWord';

console.log(hello); // 3.14

這里如果把注釋放開叠赐,同樣輸出3.14欲账, 當(dāng)有多條export default語句,只會輸出第一條export default的值

// helloWord.js
export default 3.14
export default { foo: 'bar' };
export default 'hello word';

// main.js
import hello from './helloWord';

console.log(hello); // 3.14
具名導(dǎo)入

除了默認導(dǎo)出外芭概, ES6的模塊系統(tǒng)還支持導(dǎo)出任意數(shù)量個具名的變量:

const PI = 3.14
const value = 42;
export function hello(){
    console.log('2111');
}
export {PI,value}

// 等同于CommonJS語法

// var PI = 3.14;
// var value = 42;
// module.exports.hello = function(){
//     console.log('2111');
// }
// module.exports.PI = PI;
// module.exports.value = value;

導(dǎo)入的時候:

import {PI,value,hello} from './helloWord';

console.log(PI,value,hello());
image.png

導(dǎo)入的時候可以使用as關(guān)鍵字來重命名導(dǎo)入的變量:

import {PI as PI2,value as val,hello as helloWord} from './helloWord';

console.log(PI2,val,helloWord());

結(jié)果是一樣的

導(dǎo)入所有

最簡單的赛不,在一條命令中導(dǎo)入一個模塊中所有變量的方法, 是使用*標(biāo)記罢洲。 這樣一來踢故,被導(dǎo)入模塊中所有導(dǎo)出的變量都會變成它的屬性, 默認導(dǎo)出的變量則會被置于default屬性中惹苗。

// helloWord.js
const PI = 3.14
const value = 42;
export function hello(){
    console.log('2111');
}
export {PI,value}

// main.js
import * as Hello from './helloWord';

console.log(Hello);
image.png

注意一點
import * as foo fromimport foo from的區(qū)別。 后者僅僅會導(dǎo)入默認導(dǎo)出的變量桩蓉,而前者則會在一個對象中導(dǎo)入所有,如:

// helloWord.js
const PI = 3.14
const value = 42;
const foo = {
    'a':'123'
}
export function hello(){
    console.log('2111');
}
export {PI,value,foo}

// main.js
import * as foo from './helloWord';

console.log(foo);
console.log(foo.foo);
image.png

對比四種模塊價值規(guī)范

  1. CommonJS
  2. AMD
  3. CMD
  4. ES6模塊
1. CommonJS

commonJS是服務(wù)器端的模塊化規(guī)范洽瞬, node.js 就是參照commonJS規(guī)范實現(xiàn)的本涕。commonJS中有一個全局的方法 require()用來加載模塊

function myModule(){
  this.hello = function(){
    return "hello"
  }
  this.goodbye = function(){
  return "goodbye"
  }
 }
module.exports = myModule

其實module變量代表當(dāng)前模塊
這樣就可以在其他模塊中使用這個模塊

var myModule = require('myModule');

var myModuleInstance = new myModule();
myModuleInstance.hello();
myModuleInstance.goodbye();

關(guān)于commonJS的更多,見CommonJS規(guī)范

2. AMD

commonJS定義模塊的方式和引入模塊的方式還是比較簡單的菩颖,但不適合瀏覽器端为障, 因為commonJS是同步加載的晦闰。 而AMD是異步加載的,模塊的加載不影響它后面語句的運行鳍怨。 所有依賴這個模塊的語句鹅髓,都定義在一個回調(diào)函數(shù)中京景,等到加載完成之后骗奖,這個回調(diào)函數(shù)才會運行。 這個用require.js實現(xiàn)AMD規(guī)范的模塊化执桌, 用require.config()指定引用路徑等,
通過define()來定義模塊伴逸, 用requier加載模塊

首先我們需要引入require.js文件和一個入口文件main.js. main.js中配置require.config()并規(guī)定項目中用到的基礎(chǔ)模塊膘壶。

/** 網(wǎng)頁中引入require.js及main.js **/
<script src="js/require.js" data-main="js/main"></script>

/** main.js 入口文件/主模塊 **/
// 首先用config()指定各模塊路徑和引用名
require.config({
  baseUrl: "js/lib",
  paths: {
    "jquery": "jquery.min",  //實際路徑為js/lib/jquery.min.js
    "underscore": "underscore.min",
  }
});
// 執(zhí)行基本操作
require(["jquery","underscore"],function($,_){
  // some code here
});

引用模塊的時候,我們將模塊名放在[]中作為 require() 的第一參數(shù)顷锰; 如果我們定義的模塊本身也依賴其他模塊亡问, 那就需要將他們放在[]中作為define的第一參數(shù)

// 定義math.js模塊
define(function () {
    var basicNum = 0;
    var add = function (x, y) {
        return x + y;
    };
    return {
        add: add,
        basicNum :basicNum
    };
});
// 定義一個依賴underscore.js的模塊
define(['underscore'],function(_){
  var classify = function(list){
    _.countBy(list,function(num){
      return num > 30 ? 'old' : 'young';
    })
  };
  return {
    classify :classify
  };
})

// 引用模塊,將模塊放在[]內(nèi)
require(['jquery', 'math'],function($, math){
  var sum = math.add(10,20);
  $("#sum").html(sum);
});

define的第一個參數(shù)是依賴的模塊束世, 必須是一個數(shù)組床玻。 通過return來暴露接口

通過 require() 來加載模塊, 模塊的名字默認為模塊加載器請求的指定腳本的名字

require(['main'],function(main){
    alert(main.foo());
})

require.js就是根據(jù)AMD規(guī)范來實現(xiàn)的薪丁, 優(yōu)點是:

  1. 實現(xiàn)js文件的異步加載, 避免網(wǎng)頁失去響應(yīng)
  2. 管理模塊之間的依賴性严嗜,便于代碼的編寫和維護
3. CMD

CMD也是異步模塊定義
CMD與AMD的區(qū)別:
CMD相當(dāng)于按需加載, 定義一個模塊的時候不需要立即制定依賴模塊茄蚯,在需要的時候require就可以了睦优,比較方便。
而AMD則相反汗盘,定義模塊的時候需要制定依賴模塊, 并以形參方式引入回調(diào)函數(shù)中隐孽。

// CMD 按需加載
        define(function(require,exports,module){
            var a = require('./helloWord');
            a.hello(); 

            var b = require('./counter');
            console.log(b.foo); 
            // 2111
            // aaa
        })

        // AMD 定義模塊的時候需要制定依賴
        define(['./helloWord','./counter'],function(a,b){
            a.hello();
            console.log(b.foo);
        });
image.png
ES6

ES6在語言規(guī)格的層面上菱阵,實現(xiàn)了模塊功能,而且實現(xiàn)得相當(dāng)簡單都办, 完全可以取代現(xiàn)有的CommonJS和AMD規(guī)范虑稼, 成為瀏覽器和服務(wù)器通用的模塊解決方案。

ES6模塊主要有兩個功能: exportimport

export用于對外輸出本模塊(一個文件可以理解為一個模塊)變量的接口

import用于在一個模塊中加載另一個含有exoport接口的模塊

參考:
https://segmentfault.com/a/1190000010058955
https://juejin.im/post/5aaa37c8f265da23945f365c

https://segmentfault.com/a/1190000004100661
http://es6.ruanyifeng.com/#docs/module

深入系列:
https://github.com/mqyqingfeng/Blog/issues/108
https://zhuanlan.zhihu.com/p/33843378?group_id=947910338939686912

面試題

題目一:ES6與commonJS模塊的差異

1. commonJS模塊輸出的是一個值的拷貝槽卫,ES6模塊輸出的是值的引用胰蝠。

  • commonJS模塊一旦輸出一個值,模塊內(nèi)部的變化就影響不到這個值茸塞。
  • ES6模塊如果使用import從一個模塊加載變量钾虐,那些變量不會被緩存,而是成為一個指向被加載模塊的引用效扫,原始值變了直砂,import加載的值也會跟著變浩习。需要開發(fā)者自己保證,真正取值的時候能夠取到值洽蛀。

**2. commonJS 模塊是運行時加載构哺, ES6模塊是編輯時輸出接口

  • 運行時加載: commonJS模塊就是對象验辞,即在輸入時是加載整個模塊矾柜,生成一個對象耻卡,然后再從整個對象上讀取方法头岔,這種加載稱為”運行時加載“鼠证。 commonJS腳本代碼在require的時候靠抑,就會全部執(zhí)行。一旦出現(xiàn)某個模板被”循環(huán)加載“颂碧,就只能輸出已經(jīng)執(zhí)行的部分载城,還未執(zhí)行的部分不會輸出。

  • 編譯時加載: ES6模塊不是對象诉瓦,而是通過export命令顯式指定輸出的代碼, import時指定加載某個輸出值固额,而不是加載整個模塊煞聪,這種加載稱為”編譯時加載“

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末昔脯,一起剝皮案震驚了整個濱河市笛臣,隨后出現(xiàn)的幾起案子隧饼,更是在濱河造成了極大的恐慌,老刑警劉巖桑李,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贵白,死亡現(xiàn)場離奇詭異,居然都是意外死亡禁荒,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門勃痴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沛申,“玉大人姐军,你說我怎么就攤上這事∞刃浚” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵饼丘,是天一觀的道長辽话。 經(jīng)常有香客問我,道長贴捡,這世上最難降的妖魔是什么村砂? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮汛骂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘帘瞭。我一直安慰自己蝶念,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布担敌。 她就那樣靜靜地躺著廷蓉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桃犬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天土匀,我揣著相機與錄音形用,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛躯砰,可吹牛的內(nèi)容都是我干的琢歇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼揭保,長吁一口氣:“原來是場噩夢啊……” “哼魄宏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤椭坚,失蹤者是張志新(化名)和其女友劉穎搏色,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垂涯,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡航邢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年翠忠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秽之。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡考榨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出河质,到底是詐尸還是另有隱情,我是刑警寧澤散休,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布乐尊,位于F島的核電站,受9級特大地震影響限府,放射性物質(zhì)發(fā)生泄漏痢缎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一署穗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛇捌,春花似錦、人聲如沸俭驮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逸嘀。三九已至允粤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間类垫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工残家, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留售躁,地道東北人。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓回窘,卻偏偏與公主長得像市袖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子付枫,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容