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