不知道有多少同窗像我同樣寫了好久的JS代碼😅,可是對JS的模塊化仍是不清楚,這篇文章比較適合新手朋友,通讀以後能對模塊化有一個簡單的概念。前端
模塊化,其實就是在項目中定義若干個相對獨立的可複用的模塊,模塊中包含特定的功能,當你想要用這個功能的時候,只須要引入對應的模塊便可。npm
其實模塊化主要是爲了規避和全局屬性的衝突,同時也但願寫模塊的時候能本身維護本身的模塊,多人項目開發的時候不用擔憂和別人變量衝突。編程
模塊化其實也就是某種意義上面的全局,或者和封裝代碼,只不過是封裝的稍微好一點而已,其實也不是全局變量。設計模式
在早期的時代,若是咱們想要導出一個模塊經常使用的方法就是經過閉包,加持一點設計模式來實現模塊化,比較經典的就是JQuery實現模塊的方式:前端工程化
(function(window) {
/// 代碼
window.jQuery = window.$ = jQuery; // 經過給window添加屬性而暴露到全局
})(window)
複製代碼
這種封裝模式被不少人模仿,經過匿名函數包裝代碼,所依賴的外部變量傳給這個函數,這個函數內部可使用這些依賴,而後再將模塊自身暴露給window。瀏覽器
雖然這種模式看似很方便,可是其實仍是增長了全局變量,並且當咱們要引用的時候也一樣須要注意引用模塊的順序,否則極可能致使頁面報錯。bash
這部分因爲年代久遠,並且如今在項目基本不多會使用,因此這裏做者就只把一些淺顯的知識點加以整理,算是簡單的瞭解一下吧。
AMD和CMD主要是解決在瀏覽器端的異步模塊化編程的需求,其中AMD和CMD最具表明的兩個做品分別是require.js和sea.js,有興趣的能夠詳細瞭解一下。閉包
這二者用法基本相同,區別就是AMD崇尚依賴前置,CMD崇尚的是按需加載,也就是在須要用到某個模塊的時候再進行加載。異步
defined('模塊名稱', ['依賴的模塊1', '依賴的模塊2', ], function (moudle1, moudle2) {
class item {}
return item 或者 module.exports = item
});
複製代碼
const moudle = require('./moudle');
複製代碼
這裏須要注意的是,若是當前文件是AMD一個模塊能夠像下面這樣寫:模塊化
defined('test', function () {
var a = require('a'); // 按需加載,在用到的地方在加載
});
複製代碼
若是不是,按照AMD規範須要定義成局部require
var a = require(['a'], function () {
代碼
});
複製代碼
由於這裏require多是一個異步的形式,這是就須要用回調,若是直接var a = require('a');這樣使用也成功了的話,只能證實這個模塊加載或定義過,不過在全局的話最穩妥的方式仍是上面的那種形式。
能夠思考一下當咱們在用的時候:
var utils = require('./utils');
utils.xxx();
複製代碼
這樣寫的話,JS指定是來不及加載的,可是這個語句是同步執行的,他們是怎麼作的呢????
其實他會先把咱們的函數先執行一下toString方法,拿到函數的字符串,而後用正則一下,看看哪裏用到了require
var functionStr = function.toString();
var regx = /require\(['"][^)*]['"]\)/ 複製代碼
這樣就能夠拿到咱們項目中須要require的模塊,而後就能夠在使用以前進行加載模塊了。
2009 年 ry 發佈 Node.js 的第一個版本,CommonJS 做爲其中最核心的特性之一,適用於服務端下的場景;歷年來的考察和時間的洗禮,以及前端工程化對其的充分支持,CommonJS 被普遍運用於 Node.js 和瀏覽器:
注意:這裏不要直接修改exports的值
// a.js
console.log(require('./b')); // {}
// b.js
exports = { name: 'changan' }
複製代碼
上面代碼能夠看出,這樣用的話咱們require的是一個空對象,這是爲何呢?
咱們能夠經過exports的僞代碼來看一下:
// 當咱們要導出模塊的時候,內核幫忙聲明
var exports = moudle.exports = { };
複製代碼
想象一下,當咱們進行例子中的操做的時候,至關於咱們會把exports變量覆蓋掉,可是moudle.exports並無改變,當咱們require模塊的時候仍是會找module.exports對象,因此咱們會拿到一個空對象,這部分須要注意一下。
//a.js
var obj = {
name: 1
}
module.exports = {
getName: function () {
return obj.name
},
setName: function (name) {
obj.name = name;
}
}
// ==================
// b.js
var a = require('./a');
console.log('a-1: ', a.getName()); // a-1:1
a.setName(2222);
console.log('a-2: ', a.getName()); // a-2:2222
// ==================
// c.js
var a = require('./a');
var b = require('./b');
console.log('a-3: ', a.getName()); // a-3:2222
複製代碼
這裏仍是值得注意一下的,由於commonJS引入的模塊是單例的,因此這也就解釋了爲何當兩個文件引用同一個模塊以後,一個文件改變了這個模塊的值的時候,另外一個模塊的值也是會改變。在開發中經常會由於不知道這個原理而焦頭爛額,當一個同事改變了模塊的一個值的時候,另外一個引用該模塊的地方也會改變,避免的最好辦法就是不要更改模塊內部的值。
ES Module 是語言層面的模塊化方案,由 ES 2015 提出,其規範與 CommonJS 比之 ,導出的值均可以當作是一個具有多個屬性或者方法的對象,能夠實現互相兼容。
// a.js
export let a = 1;
export function caculate() {
a++;
};
// b.js
import { a, caculate } from 'a.js';
console.log(a); // 1
caculate();
console.log(a); // 2
a = 2; // Syntax Error: "a" is read-only
複製代碼