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