成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Javascript模塊化開發(fā)基礎(chǔ)

Kerr1Gan / 3024人閱讀

摘要:認(rèn)識(shí)模塊作為一名編程語言,一直以來沒有模塊的概念。在之前,有主要的個(gè)模塊化方案和。這樣引入模塊和引入模塊方法差不多,其代表是。關(guān)鍵字用于規(guī)定模塊的對(duì)外接口,關(guān)鍵字用于輸入其他模塊提供的功能。

認(rèn)識(shí)模塊

JS 作為一名編程語言,一直以來沒有模塊的概念。嚴(yán)重導(dǎo)致大型項(xiàng)目開發(fā)受阻,js 文件越寫越大,不方便維護(hù)。其他語言都有模塊的接口,比如 Ruby 的 require,python 的 import,C++ 天生的 #include,甚至 CSS 都有 @import。在 ES6 之前,有主要的2個(gè)模塊化方案:CommonJS 和 AMD。前者用于服務(wù)器,后者用于瀏覽器。CommonJS 這樣引入模塊:

let {stat, exists, readFile} = require("fs");

AMD 和 CommonJS 引入模塊方法差不多,其代表是 require.js。這里我們主要研究 ES6 提供的方法:

import {stat, exists, readFile} from "fs"

這個(gè)方法相比之前的方案,具有以下優(yōu)點(diǎn):

最大的優(yōu)點(diǎn)就是編譯的時(shí)候完成模塊加載,稱之為"編譯時(shí)加載", 而 CommonJS 使用的是 "運(yùn)行時(shí)加載"。明顯 ES6 效率更高

不再需要 UMD 模塊格式,未來服務(wù)器和瀏覽器一定都能支持這種方法

將來瀏覽器 API 可以用模塊的格式提供,不需要做成全局變量或 navigator 的屬性

不需要反復(fù)的封裝和定義命名空間,直接以模塊形式提供即可

模塊默認(rèn)工作在嚴(yán)格模式,即使沒有指定"use strict", 關(guān)于嚴(yán)格模式可以看:Javascript基礎(chǔ)(2) - 嚴(yán)格模式特點(diǎn)

一個(gè)模塊就是一個(gè)文件,有效地減少了全局變量污染

export 和 import

模塊功能主要由2個(gè)命令組成:export 和 import。export 關(guān)鍵字用于規(guī)定模塊的對(duì)外接口,import 關(guān)鍵字用于輸入其他模塊提供的功能。這里需要知道的是,ES6 中模塊導(dǎo)出的都會(huì)構(gòu)成一個(gè)對(duì)象。

export 導(dǎo)出模塊的部分方法屬性或類

export var a = 1;
export var b = 2;
export var c = 3;

上面導(dǎo)出了3個(gè)變量,和下面的下法等價(jià):

var a = 1;
var b = 2;
var c = 3;
export {a, b, c};    //這種寫法更好,在文件結(jié)尾統(tǒng)一導(dǎo)出,清晰明了

當(dāng)然還可以導(dǎo)出函數(shù)和類

//導(dǎo)出一個(gè)函數(shù) add
export function add(x,y){
  return x + y;
}
//導(dǎo)出一個(gè)類
export default class Person{}

還可以在導(dǎo)出時(shí)候?qū)?shù)重命名:

function foo(){}
function bar(){}

export {foo, bar as bar2, bar as bar3}     //bar 被重命名為 bar2,bar3輸出了2次

import 導(dǎo)入命令可以導(dǎo)入其他模塊通過 export 導(dǎo)出的部分

// abc.js
var a = 1;
var b = 2;
var c = 3;
export {a, b, c}

//main.js
import {a, b, c} from "./abc";      //接受的變量用大括號(hào)表示,以解構(gòu)賦值的形式獲取
console.log(a, b, c);

導(dǎo)入的時(shí)候也可以為變量重新取一個(gè)名字

import {a as aa, b, c};
console.log(aa, b, c)

如果想在一個(gè)模塊中先輸入后輸出同一個(gè)模塊,import語句可以和export語句寫在一起。

// 正常寫法
import {a, b, c} form "./abc";
export {a, b, c}

// 使用簡(jiǎn)寫, 可讀性不好,不建議
export {a, b, c} from "./abc";

//ES7 提議,在簡(jiǎn)化先輸入后輸出的寫法。現(xiàn)在不能使用,也不建議使用,可讀性不好
export a, b, c from "./abc"

使用 import 和 export 需要注意一下幾個(gè)方面:

export 必須寫在所在模塊作用于的頂層。如果寫在了內(nèi)部作用于會(huì)報(bào)錯(cuò)

export 輸出的值是動(dòng)態(tài)綁定的,綁定在其所在的模塊。

// foo.js
export var foo = "foo";

setTimeout(function() {
  foo = "foo2";
}, 500);

// main.js
import * as m from "./foo";
console.log(m.foo); // foo
setTimeout(() => console.log(m.foo), 500); //foo2            500ms 后同樣會(huì)被修改

import 具有聲明提升,而且會(huì)提升到整個(gè)文件最上面

import 獲得的變量都是只讀的,修改它們會(huì)報(bào)錯(cuò)

在 export 輸出內(nèi)容時(shí),如果同時(shí)輸出多個(gè)變量,需要使用大括號(hào){},同時(shí) import 導(dǎo)入多個(gè)變量也需要大括號(hào)

import 引入模塊的默認(rèn)后綴是 .js, 所以寫的時(shí)候可以忽略 js 文件擴(kuò)展名

import 會(huì)執(zhí)行要所加載的模塊。如下寫法僅僅執(zhí)行一個(gè)模塊,不引入任何值

import "./foo";    //執(zhí)行 foo.js 但不引入任何值
模塊整體加載

當(dāng)然模塊可以作為整體加載,使用*關(guān)鍵字,并利用 as 重命名得到一個(gè)對(duì)象,所有獲得的 export 的函數(shù)、值和類都是該對(duì)象的方法:

// abc.js
export var a = 1;
export var b = 2;
export var c = 3;

// main.js
import * as abc from "./abc";
console.log(abc.a, abc.b, abc.c);

上面 main.js 中的整體加載可以用 module 關(guān)鍵字實(shí)現(xiàn):

//暫時(shí)無法實(shí)現(xiàn)
module abc from "./abc";
console.log(abc.a, abc.b, abc.c);   //1 2 3

注意,以上2種方式獲得的接口,不包括 export default 定義的默認(rèn)接口。

export default

為了使模塊的用戶可以不看文檔,或者少看文檔,輸出模塊的時(shí)候利用 export default 指定默認(rèn)輸出的接口。使用 export defalut 輸出時(shí),不需要大括號(hào),而 import 輸入變量時(shí),也不需要大括號(hào)(沒有大括號(hào)即表示獲得默認(rèn)輸出)

// abc.js
var a = 1, b = 2, c = 3;
export {a, b};
export default c;     //等價(jià)于 export default 3;

// main.js
import {a, b} from "./abc";
import num from "./abc";        // 不需要大括號(hào), 而且可以直接改名(如果必須用原名不還得看手冊(cè)么?)
console.log(a, b, num)            // 1 2 3

本質(zhì)上,export default輸出的是一個(gè)叫做default的變量或方法,輸入這個(gè)default變量時(shí)不需要大括號(hào)。

// abc.js
var a = 20;
export {a as default};

// main.js
import a from "./abc"; // 這樣也是可以的
console.log(a);        // 20

// 這樣也是可以的
import {default as aa} from "./abc";
console.log(aa);       // 20

如果需要同時(shí)輸入默認(rèn)方法和其他變量可以這樣寫 import:

import customNameAsDefaultExport, {otherMethod}, from "./export-default";

這里需要注意:一個(gè)模塊只能有一個(gè)默認(rèn)輸出,所以 export default 只能用一次

模塊的繼承

所謂模塊的繼承,就是一個(gè)模塊 B 輸出了模塊 A 全部的接口,就仿佛是 B 繼承了 A。利用 export * 實(shí)現(xiàn):

// circleplus.js
export * from "circle";            //當(dāng)然,這里也可以選擇只繼承其部分接口,甚至可以對(duì)接口改名
export var e = 2.71828182846;
export default function(x){        //重新定義了默認(rèn)輸出,如果不想重新定義可以:export customNameAsDefaultExport from "circle";
  return Math.exp(x);
}

//main.js
import * from "circleplus";        //加載全部接口
import exp from "circleplus";      //加載默認(rèn)接口
//...use module here

上面這個(gè)例子 circleplus 繼承了 circle。值得一提的是,export * 不會(huì)再次輸出 circle 中的默認(rèn)輸出(export default)。

在使用和定義模塊時(shí),希望可以做到以下幾個(gè)建議:

Module 語法是 JavaScript 模塊的標(biāo)準(zhǔn)寫法,堅(jiān)持使用這種寫法。使用 import 取代 require, 使用 export 取代module.exports

如果模塊只有一個(gè)輸出值,就使用 export default,如果模塊有多個(gè)輸出值,就不使用 export default

盡量不要 export default 與普通的 export 同時(shí)使用

不要在模塊輸入中使用通配符。因?yàn)檫@樣可以確保你的模塊之中,有一個(gè)默認(rèn)輸出(export default)

如果模塊默認(rèn)輸出一個(gè)函數(shù),函數(shù)名的首字母應(yīng)該小寫;如果模塊默認(rèn)輸出一個(gè)對(duì)象,對(duì)象名的首字母應(yīng)該大寫

ES6 模塊加載的實(shí)質(zhì)

ES6 模塊加載的機(jī)制是值的應(yīng)用,而 CommonJS 是值的拷貝。這意味著, ES6 模塊內(nèi)的值的變換會(huì)影響模塊外對(duì)應(yīng)的值,而 CommonJS 不會(huì)。 ES6 遇到 import 時(shí)不會(huì)立刻執(zhí)行這個(gè)模塊,只生成一個(gè)動(dòng)態(tài)引用,需要用的時(shí)候再去里面找值。有點(diǎn)像 Unix 中的符號(hào)鏈接。所以說 ES6的模塊是動(dòng)態(tài)引用,不會(huì)緩存值。之前的這個(gè)例子就可以說明問題:

// foo.js
export let counter = 3;
export function inc(){
  counter++;
}

// main.js
import {counter, inc} from "./foo";
console.log(counter);    //3
inc();
console.log(counter);    //4

我們看一個(gè) CommonJS 的情況

// foo.js
let counter = 3;
function inc(){
  counter++;
}
module.exports = {
  counter: counter,
  inc: inc
}

// main.js
let foo = require("./foo")
let counter = foo.counter;
let inc = foo.inc;

console.log(counter);    //3
inc();
console.log(counter);    //3
循環(huán)加載

不知道你們只不知道循環(huán)引用,在內(nèi)存管理與垃圾回收中提到過:如果 A 對(duì)象的一個(gè)屬性值是 B 對(duì)象,而 B 對(duì)象的一個(gè)屬性值是 A 對(duì)象,就會(huì)形成循環(huán)引用,無法釋放他們的內(nèi)存。而模塊中也會(huì)出現(xiàn)循環(huán)加載的情況:如果 A 模塊的執(zhí)行依賴 B 模塊,而 B 模塊的執(zhí)行依賴 A 模塊,就形成了一個(gè)循環(huán)加載,結(jié)果程序不能工作,或者死機(jī)。然而,這樣的關(guān)系很難避免,因?yàn)殚_發(fā)者眾多,誰都會(huì)在開發(fā)自己的模塊時(shí)使用別人的幾個(gè)模塊,久而久之,就行互聯(lián)網(wǎng)一樣,這樣的依賴也織成了一個(gè)網(wǎng)。

ES6 和 CommonJS 處理循環(huán)加載又不一樣,從 CommonJS 開始研究

CommonJS

CommonJS 每次執(zhí)行完一個(gè)模塊對(duì)應(yīng)的 js 文件后在內(nèi)存中就生成一個(gè)對(duì)象:

{
  id: "...",           //表示屬性的模塊名
  exports: {...};      //模塊輸出的各個(gè)接口
  loaded: true,        //表示是否加載完畢
  //...內(nèi)容很多,不一一列舉了
}

之后使用這個(gè)模塊,即使在寫一遍 requrie,都不會(huì)再執(zhí)行對(duì)應(yīng) js 文件了,會(huì)直接在這個(gè)對(duì)象中取值。
CommonJS 如果遇到循環(huán)加載,就輸出已執(zhí)行的部分,之后的不再執(zhí)行,執(zhí)行順序以注釋序號(hào)為準(zhǔn)(從0開始):

// a.js
exports.done = false;         //1. 先輸出 done
var b = require("./b.js");    //2. 進(jìn)入 b.js 執(zhí)行 b.js    //5. 發(fā)現(xiàn) a.js 沒執(zhí)行完,那就重復(fù)不執(zhí)行 a.js,返回已經(jīng)執(zhí)行的 exports
console.log(`In a.js, b.done = ${b.done}`);     //10. 第2步的 b.js 執(zhí)行完了,繼續(xù)執(zhí)行 a.js 得到控制臺(tái)輸出:"In a.js, b.done = true"
exports.done = true;          //11
console.log("a.js executed");  //12. 得到控制臺(tái)輸出:"a.js executed"

// b.js
exports.done = false;         //3. 先輸出 done
var a = require("./a.js");    //4. 執(zhí)行到這里發(fā)生循環(huán)加載,去 a.js 執(zhí)行 a.js     //6. 只得到了 a.js 中的 done 為 false
console.log(`In b.js, a.done = ${a.done}`);       //7. 得到控制臺(tái)輸出:"In b.js, a.done = false"
exports.done = true;     //8. 輸出 done, 覆蓋了第3步的輸出
console.log("b.js executed");     //9. 得到控制臺(tái)輸出:"b.js executed"

//main.js
var a = require("./a.js");    //0. 去 a.js 執(zhí)行 a.js
var b = require("./b.js");    //13. b.js 已經(jīng)執(zhí)行過了,直接去內(nèi)存中的對(duì)象取值
console.log(`In main,a.done = ${a.done}, b.done = ${b.done}`)    //得到控制臺(tái)輸出:"In main,a.done = true, b.done = true"

ES6

由于 ES6 使用的是動(dòng)態(tài)引用,遇到 import 時(shí)不會(huì)執(zhí)行模塊。所以和 CommonJS 有本質(zhì)的區(qū)別。同樣我們看個(gè)例子:

// a.js
import {bar} from "./b.js";
export function foo(){
  bar();
  console.log("finished")
}

// b.js
import {foo} from "./a.js";
export function bar(){
  foo();
}

//main.js
import * from "./a.js";
import * from "./b.js";
//...

上面這段代碼寫成 CommonJS 形式是無法執(zhí)行的,應(yīng)為 a 輸出到 b 的接口為空(null), 所以在 b 中調(diào)用 foo() 要報(bào)錯(cuò)的。但是 ES6 可以執(zhí)行,得到控制臺(tái)輸出"finished"

另一個(gè)例子是這樣的。執(zhí)行順序以注釋序號(hào)為準(zhǔn)(從0開始):

// even.js
import {odd} from "./odd";         //2. 得到 odd.js 動(dòng)態(tài)引用,但不執(zhí)行
export var counter = 0;            //3. 輸出 counter 的引用
export function even(n){           //4. 輸出 even 函數(shù)的引用
  counter++;                       //6
  return n === 0 || odd(n - 1);    //7. n 不是 0, 去 odd.js 找 odd() 函數(shù)    //10. 執(zhí)行 odd 函數(shù),傳入9
}

// odd.js
import {even} from "./even";       //8. 得到 even.js 動(dòng)態(tài)引用,但不執(zhí)行
export function odd(n){            //9. 輸出 odd 函數(shù)
  return n !== 0 && even(n - 1);   //11. 回到第2步,找到 even 函數(shù),回來執(zhí)行,傳入8,直到 n 為 0 結(jié)束
}

// main.js
import * as m from "./even";    //0. 得到 even.js 動(dòng)態(tài)引用,但不執(zhí)行
console.log(m.even(10));     //1. 去 even.js 找 even 函數(shù)。 //5. 執(zhí)行函數(shù),傳入10   //最終得到控制臺(tái)輸出:true
console.log(m.counter);      //由于 ES6 模塊傳值是動(dòng)態(tài)綁定的(下同),所以得到控制臺(tái)輸出:6
console.log(m.even(20));     //分析同上,得到控制臺(tái)輸出:true
console.log(m.counter);      //得到控制臺(tái)輸出:17

上面寫了11步,之后是一個(gè)循環(huán),沒有繼續(xù)寫。但不難看出 ES6 根本不怕循環(huán)引用,只要模塊文件的動(dòng)態(tài)引用在,就可以計(jì)算完成。不過,別看這個(gè)過程比 CommonJS 復(fù)雜,每次都有重新運(yùn)行模塊文件,而不直接讀取緩存,但 ES6 的這些工作在編譯期間就完成了,比 CommonJS 在運(yùn)行時(shí)間處理模塊要效率更高,體驗(yàn)更好。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/97457.html

相關(guān)文章

  • OMD: javascript塊化開發(fā)兼容CommonJS, AMD, CMD 以及 原生 JS

    摘要:它就是一套兼容方案,目前兼容的有以及原生支持。返回值問題在第一次使用時(shí),。具體是什么意義呢的返回值,其實(shí)就是插件提供的對(duì)外接口,而實(shí)際上,就是一個(gè)對(duì)象。而在環(huán)境下,只需要將這個(gè)返回值賦予即可完成該模塊的接口。 有更新,請(qǐng)到github上看源碼 什么是OMD 在node.js流行起來之前,javascript的開發(fā)方式都是函數(shù)式的順序依賴關(guān)系,直到node火起來。CommonJS其實(shí)首先...

    lavor 評(píng)論0 收藏0
  • 前端資源系列(4)-前端學(xué)習(xí)資源分享&前端面試資源匯總

    摘要:特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 本以為自己收藏的站點(diǎn)多,可以很快搞定,沒想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補(bǔ)充。有錯(cuò)誤的地方,還請(qǐng)斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會(huì)及時(shí)更新,平時(shí)業(yè)務(wù)工作時(shí)也會(huì)不定期更...

    princekin 評(píng)論0 收藏0
  • webpack 3 零基礎(chǔ)入門教程 #1 - 介紹

    摘要:所以它在某些程度上,跟的功能有些相同。嚴(yán)格上講,模塊化不是他強(qiáng)調(diào)的東西,他旨在規(guī)范前端開發(fā)流程。更是明顯強(qiáng)調(diào)模塊化開發(fā),而那些文件壓縮合并預(yù)處理等功能,不過是他附帶的功能。 1. webpack 是什么? showImg(https://segmentfault.com/img/remote/1460000012293461); 先來說一下 webpack 是什么。 webpack 的...

    張紅新 評(píng)論0 收藏0
  • 前端塊化雜談

    摘要:并不是使用安裝的模塊我們就可以使用同樣的方式使用任何一個(gè)模塊,使用某種工具將這些模塊打包發(fā)布作為事實(shí)上的前端模塊化標(biāo)準(zhǔn),或可以出來解救我們。目前比較拿的出手的,也就是的模塊化,比如或者等等,分別可以使用和。 Teambition是一家追求卓越技術(shù)的公司,我們工程師都很Geek,我們使用了很多新潮的,開源的技術(shù)。同時(shí)我們也貢獻(xiàn)了很多開源的項(xiàng)目。我們希望能夠把一些技術(shù)經(jīng)驗(yàn)分享給大家。...

    yacheng 評(píng)論0 收藏0
  • 前端塊化雜談

    摘要:并不是使用安裝的模塊我們就可以使用同樣的方式使用任何一個(gè)模塊,使用某種工具將這些模塊打包發(fā)布作為事實(shí)上的前端模塊化標(biāo)準(zhǔn),或可以出來解救我們。目前比較拿的出手的,也就是的模塊化,比如或者等等,分別可以使用和。 Teambition是一家追求卓越技術(shù)的公司,我們工程師都很Geek,我們使用了很多新潮的,開源的技術(shù)。同時(shí)我們也貢獻(xiàn)了很多開源的項(xiàng)目。我們希望能夠把一些技術(shù)經(jīng)驗(yàn)分享給大家。...

    li21 評(píng)論0 收藏0
  • 前端每周清單第 44 期: 2017 JS 調(diào)查報(bào)告、REST 接口實(shí)時(shí)化、ESM 的過去與未來

    摘要:巔峰人生年老兵思路上的轉(zhuǎn)變,遠(yuǎn)比單純提升技術(shù)更有價(jià)值本文節(jié)選自趙成教授在極客時(shí)間開設(shè)的趙成的運(yùn)維體系管理課,是其對(duì)自己十年技術(shù)生涯的回顧與總結(jié)。趙成教授來自美麗聯(lián)合集團(tuán),集團(tuán)旗下兩大主力產(chǎn)品是蘑菇街和美麗說,目前負(fù)責(zé)管理集團(tuán)的技術(shù)服務(wù)團(tuán)隊(duì)。 showImg(https://segmentfault.com/img/remote/1460000012476504?w=1240&h=826...

    MASAILA 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<