摘要:模塊的加載第一個參數(shù),是一個數(shù)組,里面的成員就是要加載的模塊第二個參數(shù),則是加載成功之后的回調(diào)函數(shù)。異步加載,瀏覽器不會失去響應(yīng)它指定的回調(diào)函數(shù),只有前面的模塊都加載成功后,才會運行,解決了依賴性的問題。
什么是模塊化?
模塊化就是把系統(tǒng)分離成獨立功能的方法,這樣我們需要什么功能,就加載什么功能。
優(yōu)點:
可維護(hù)性:根據(jù)定義,每個模塊都是獨立的,良好設(shè)計的模塊會盡量與外部的代碼撇清關(guān)系,以便于獨立對其進(jìn)行改進(jìn)和維護(hù)。
可復(fù)用性:可以重復(fù)利用,而不用經(jīng)常復(fù)制自己之前寫過的代碼
1、污染全局變量
//a.js 文件:
var test1="aaaaaa"; //b.js 文件 var test1="bbbbbb"; console test1 輸出"bbbbbb";悲劇啊
2、命名沖突
//a.js 文件: function fun(){ console.log("this is b"); } //b.js 文件 function fun(){ console.log("this is b"); } //main.js 文件 小張在a.js定義了fun(),小李在b.js又定義了fun(),a,b被小王引入到main.js,執(zhí)行fun(),輸出this is b;
3、依賴關(guān)系
b.js依賴a.js,標(biāo)簽的書寫順序必須是:
這樣在多人開發(fā)的時候很難協(xié)調(diào)啊,令人頭疼的問題。
解決沖突的方式1、使用java式的命名空間
2、變量前加“_”
3、對象寫法
var module1={ test1:"aaaaaa", fun:function(){ console.log(this.test1); } } 變量和函數(shù)封裝在對象里面,使用時,調(diào)用對象的屬性即可: module1.fun();//aaaaaa 但是這樣的寫法會暴露所有模塊成員,內(nèi)部狀態(tài)可以被外部改寫, module1.test1="cccccc";
4、匿名閉包函數(shù)
var module1=(function(){ var test1="aaaaaa"; var fun=function(){ console.log("this is a"); } return{ fun:fun } }());
匿名函數(shù)有自己的作用域,這樣外部代碼無法讀取 module1 function 里面的變量了,從而也不會修改變量或者是覆蓋同名變量了,但是還是有缺陷的,module1這個的變量還是暴露到全局了,而去隨著模塊的增多,全局變量會越來越多。
5、全局引入
像jquery庫使用的全局引入。和匿名閉包函數(shù)相似,只是傳入全局變量的方法不同
(function(window){
var test1="aaaaaa"; window.testFun=function(){//通過給window添加屬性而暴漏到全局 console.log(test1); }
}(window));
通過匿名函數(shù)包裝代碼,所依賴的外部變量傳給這個函數(shù),在函數(shù)內(nèi)部可以使用這些依賴,然后在函數(shù)的最后把模塊自身暴漏給window。
3,4,5解決方法都是通過定一個全局變量來把所有的代碼包含在一個函數(shù)內(nèi),由此來創(chuàng)建私有的命名空間和閉包作用域。
本文著重介紹幾種廣受歡迎的解決方案:CommonJS,AMD,CMD,ES模塊化。
CommonJs根據(jù)CommonJs規(guī)范,每個文件就是一個模塊,有自己的作用域。在一個文件里面定義的變量、函數(shù)、類,都是私有的,對其他文件不可見。
commonJS中模塊可以加載多次,但是只會在第一次加載的時候運行一次,然后運行結(jié)構(gòu)被緩存,再次加載就是讀取緩存的結(jié)果。
CommonJS規(guī)范加載模塊是同步的,也就是說,加載完成才可以執(zhí)行后面的操作,Node.js主要用于服務(wù)器編程,模塊一般都是存在本地硬盤中,加載比較快,所以Node.js采用CommonJS規(guī)范。
CommonJS規(guī)范分為三部分:module(模塊標(biāo)識),require(模塊引用), exports(模塊定義),
module變量在每個模塊內(nèi)部,就代表當(dāng)前模塊;
exports屬性是對外的接口,用于導(dǎo)出當(dāng)前模塊的方法或變量;
require()用來加載外部模塊,讀取并執(zhí)行js文件,返回該模塊的exports對象;
module.exports定義模塊:
//math.js let add=(x,y)=>{ return x+y; } let sub=(x,y)=>{ return x-y; } module.exports={ add:add, sub:sub };
exports 定義模塊:
let add=(x,y)=>{ return x+y; } let sub=(x,y)=>{ return x-y; } exports.add=add; exports.sub=sub;
注意:不可以直接對exports賦值,exports=add;
exports和module.exports有什么區(qū)別呢?
在每個模塊中Node都提供了一個Module 對象,代表當(dāng)前模塊。
//console.log(Module); Module { id: ".", exports: {}, parent: null, filename: "/Users/zss/node-Demo/my-app/testNOde/b.js", loaded: false, children: [], paths: [ "/Users/zss/node-Demo/my-app/testNOde/node_modules", "/Users/zss/node-Demo/my-app/node_modules", "/Users/zss/node-Demo/node_modules", "/Users/zss/node_modules", "/Users/node_modules", "/node_modules" ] }
module.exports屬性表示當(dāng)前模塊對外輸出的接口,其他文件加載該模塊,實際上就是讀取module.exports變量。
為了方便,Node為每個模塊提供一個exports變量,指向module.exports。我們把它們都打印出來看看究竟,
//test.js console.log(module.exports); console.log(exports); console.log(module.exports===exports); exports.test = ()=>{ console.log("exports 1"); }; module.exports.test1 = ()=>{ console.log("module.exports 1"); }; console.log(module.exports); console.log(exports); //輸出: {} {} true { test: [Function], test1: [Function] } { test: [Function], test1: [Function] }
從上例可以看出:
1.每個模塊文件一創(chuàng)建,有個var exports = module.exports = {};使exports和module.exports都指向一個空對象。
**2.module是全局內(nèi)置對象,exports是被var創(chuàng)建的局部對象,module.exports和exports所指向的內(nèi)存地址相同
所有的exports收集到的屬性和方法,都賦值給了Module.exports,最終返回給模塊調(diào)用的是module.exports而不是exports。**
再舉個例子:
//test.js exports.test = ()=>{ console.log("exports 1"); }; module.exports={ test:function(){ console.log("module.exports 1"); }, testmodule:()=>{ console.log("module.exports 2") } } console.log(module.exports); console.log(exports); //輸出 { test: [Function: test], testmodule: [Function: testmodule] } { test: [Function] } //在index.js文件中調(diào)用test2.js let a=require("./test2"); a.test(); a.testmodule(); //輸出: module.exports 1 module.exports 2
所有的exports收集到的屬性和方法,都賦值給了Module.exports,當(dāng)直接把函數(shù)和屬性傳給module.exports時,module.exports與exports不想等了,在調(diào)用時候,exports的屬性和方法會被忽略,所以最終返回給模塊調(diào)用的是module.exports而不是exports。
2、模塊分類NodeJs的模塊分為兩類:
一類是原生模塊,例如http,fs,path 等等。node在加載原生模塊的時候,不需要傳入路徑,NodeJs將原生模塊的代碼編譯到了二進(jìn)制執(zhí)行文件中,加載速度快。
一類是文件模塊,動態(tài)加載模塊,
但是NodeJs對原生模塊和文件模塊都進(jìn)行了緩存,第二次require時,就是執(zhí)行的內(nèi)存中的文件。
index.js調(diào)用math模塊:
let math=require("./math"); let test=math.add(3,3); console.log(test);
執(zhí)行index.js 輸出:6;
當(dāng)我們執(zhí)行node index.js的時候,第一語句就是“require("./math");” 加載 math文件。加載math文件這個動作是由原生模塊module的runMain()實現(xiàn)的。
有沒有注意到上面寫的是加載math文件,并沒有明確指出是js文件。
NodeJS加載文件模塊基本流程:
1、根據(jù)名稱按照‘.js’,‘.node‘,’.json‘的順訊依次查找,如果是.node或者.json的文件最好加上擴展名,加載速度快。
2、查找到math.js,讀取js內(nèi)容,將使用function進(jìn)行包裝,這樣可以避免污染全局環(huán)境,該函數(shù)的參數(shù)包括require、module、exports等等參數(shù),以mathi.js為例:
(function(exports,require,module,__filename,__dirname){ let add=(x,y)=>{ return x+y; } let sub=(x,y)=>{ return x-y; } module.exports={ add:add, sub:sub }; })
require 方法中的文件查找規(guī)則很復(fù)雜底,在網(wǎng)上copy了一個圖:
更詳細(xì)的加載規(guī)則可以參考:http://www.infoq.com/cn/artic...
4、commonJs模塊的加載機制://lib.js var counter = 3; function incCounter() { counter++; } module.exports = { counter: counter, incCounter: incCounter, }; //index.js var mod=require("./lib"); consoe.log(mod.counter); mod.incCounter(); consoe.log(mod.counter); 輸出:3 3
commonJS中模塊加載以后,它的內(nèi)部變化不會影響其內(nèi)部變量,因為它們會被緩存,所以它輸出的是值的拷貝。
CommonJS規(guī)范比較適用服務(wù)器端,如果是瀏覽器就需要異步加載模塊了,所以就有了AMD,CMD解決方案。
AMD(requireJS)AMD是"Asynchronous Module Definition"的簡寫,也就是異步模塊定義。它采用異步方式加載模塊。通過define方法去定義模塊,require方法去加載模塊。
AMD模塊定義:define(function(){ let add=(x,y)=>{ return x+y; } let sub=(x,y)=>{ return x-y; } return { add:add, sub:sub }; });
如果這個模塊還需要依賴其他模塊,那么define函數(shù)的第一個參數(shù),必須是一個數(shù)組,指明該模塊的依賴。
define([tools],function(){ //………………………… })AMD模塊的加載:
require([module], callback);
第一個參數(shù)[module],是一個數(shù)組,里面的成員就是要加載的模塊;第二個參數(shù)callback,則是加載成功之后的回調(diào)函數(shù)。例如加載math.js。
require([math],function(){ //…………………… })
require()異步加載math,瀏覽器不會失去響應(yīng);它指定的回調(diào)函數(shù),只有前面的模塊都加載成功后,才會運行,解決了依賴性的問題。
CMD(SeaJS)玉伯提出的CMD規(guī)范,并開發(fā)了前端模塊化開發(fā)框架SeaJS,不過在2015年后SeaJS停止了在github上維護(hù),CMD與AMD用法很相似,但是我個人更喜歡使用SeaJS,雖然在2016年后也被我拋棄啦。
SeaJs使用:
// 所有模塊都通過 define 來定義 define(function(require, exports, module) { // 通過 require 引入依賴 var $ = require("jquery"); var Spinning = require("./spinning"); // 通過 exports 對外提供接口 exports.doSomething = ... // 或者通過 module.exports 提供整個接口 module.exports = ... });
有關(guān)于SeaJS與 RequireJS 的異同,可以參考:
https://github.com/seajs/seaj...
https://www.douban.com/note/2...
在es6 之前沒有模塊化的,為了解決問題,提出了commonJS,AMD,CMD,現(xiàn)在ES6模塊化汲取了CommonJS 和 AMD 的優(yōu)點,簡潔的語法,異步加載
它完全可以成為瀏覽器和服務(wù)器通用的模塊化解決方案。
ES6 新增了兩個關(guān)鍵字 export 和 import,export 用于把 模塊里的內(nèi)容 暴露 出來, import 用于引入模塊提供的功能。
export命令輸出變量:
//lib.js let bar=function(){ console.log("this is bar funciton"); }; let foo=function(){ console.log("this is foo function"); }; export {bar,foo}
上面的代碼還有另一種寫法:
export let bar=function(){ console.log("this is bar funciton"); }; export let foo=function(){ console.log("this is foo function"); };
export 不止可以導(dǎo)出函數(shù),還可以導(dǎo)出對象,類,字符串等等
const test="aaa"; const obj={ str:"hello!" } export {test,obj};
注:使用export在尾部輸出變量時,一定要加大括號,
ES6中模塊的加載import 加載模塊:
//加載 lib.js文件 import {bar,foo,test,obj} from "./lib" foo();//this is foo function
注:import 命令具有提升效果,會提升到整個模塊的頭部,首先執(zhí)行
上面的是逐一指定要加載的方法,我們還可以使用 * 可以整體加載模塊:
import * as lib from "./lib" lib.foo();
上面的加載模塊的方式需要知道變量名和函數(shù)名,否則是無法加載的,我們可以使用export default 命令,為模塊指定默認(rèn)輸出。
//lib.js let foo=function(){ console.log("this is foo"); } export default foo;
其他文件加載時,可以為該匿名函數(shù)指定任意名字。
import lib from "lib";
注:export default 命令適用于指定默認(rèn)模塊的輸出,一個模塊只能有一個默認(rèn)輸出,所以export default 只能使用一次。
ES6 模塊運行機制ES6模塊是動態(tài)引用,如果使用import從一個模塊加載變量(即import foo from "foo"),變量不會被緩存,而是成為一個指向被加載模塊的引用。等腳本執(zhí)行時,根據(jù)只讀引用,到被加載的那個模塊中去取值。
舉一個NodeJS模塊化的例子:
//lib.js export let counter = 3; exoprt function incCounter() { counter++; } module.exports = { counter: counter, incCounter: incCounter, }; //index.js import {counter,incCounter} from "./lib"; consoe.log(mod.counter); mod.incCounter(); consoe.log(mod.counter); 輸出:3 4 調(diào)用 incCounter()方法后,lib 模塊里的counter變量值改變了。
參考:
http://www.cnblogs.com/TomXu/...
http://blog.csdn.net/tyro_jav...
http://javascript.ruanyifeng....
http://www.ruanyifeng.com/blo...
https://zhuanlan.zhihu.com/p/...
https://segmentfault.com/a/11...
http://web.jobbole.com/83761/
http://es6.ruanyifeng.com/#do...
http://www.cnblogs.com/lishux...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/88428.html
摘要:感謝王下邀月熊分享的前端每周清單,為方便大家閱讀,特整理一份索引。王下邀月熊大大也于年月日整理了自己的前端每周清單系列,并以年月為單位進(jìn)行分類,具體內(nèi)容看這里前端每周清單年度總結(jié)與盤點。 感謝 王下邀月熊_Chevalier 分享的前端每周清單,為方便大家閱讀,特整理一份索引。 王下邀月熊大大也于 2018 年 3 月 31 日整理了自己的前端每周清單系列,并以年/月為單位進(jìn)行分類,具...
摘要:模塊化是隨著前端技術(shù)的發(fā)展,前端代碼爆炸式增長后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調(diào)也不等同于異步。將會討論安全的類型檢測惰性載入函數(shù)凍結(jié)對象定時器等話題。 Vue.js 前后端同構(gòu)方案之準(zhǔn)備篇——代碼優(yōu)化 目前 Vue.js 的火爆不亞于當(dāng)初的 React,本人對寫代碼有潔癖,代碼也是藝術(shù)。此篇是準(zhǔn)備篇,工欲善其事,必先利其器。我們先在代...
摘要:作為構(gòu)造函數(shù)使用,綁定到新創(chuàng)建的對象。內(nèi)部實現(xiàn)類和類的繼承構(gòu)造函數(shù)構(gòu)造函數(shù)調(diào)用父類構(gòu)造函數(shù)參考請盡可能詳盡的解釋的工作原理的原理簡單來說通過對象來向服務(wù)器發(fā)異步請求,從服務(wù)器獲得數(shù)據(jù),然后用來操作而更新頁面。 1 . 請解釋事件代理 (event delegation) 當(dāng)需要對很多元素添加事件的時,可以通過將事件添加到它們的父節(jié)點通過委托來觸發(fā)處理函數(shù)。其中利用到了瀏覽器的事件冒泡機...
摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點分為新聞熱點開發(fā)教程工程實踐深度閱讀開源項目巔峰人生等欄目。對該漏洞的綜合評級為高危。目前,相關(guān)利用方式已經(jīng)在互聯(lián)網(wǎng)上公開,近期出現(xiàn)攻擊嘗試爆發(fā)的可能。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點;分為新聞熱點、開發(fā)教程、工程實踐、深度閱讀、開源項目、巔峰人生等欄目。歡...
摘要:特意對前端學(xué)習(xí)資源做一個匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 特意對前端學(xué)習(xí)資源做一個匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會及時更新,平時業(yè)務(wù)工作時也會不定期更...
閱讀 1376·2021-11-04 16:11
閱讀 3036·2021-10-12 10:11
閱讀 2969·2021-09-29 09:47
閱讀 1608·2021-09-22 15:40
閱讀 1007·2019-08-29 15:43
閱讀 2798·2019-08-29 13:50
閱讀 1572·2019-08-29 13:28
閱讀 2685·2019-08-29 12:54