隨着JavaScript開發變得愈來愈廣泛,命名空間和依賴性變得愈來愈難以處理。前端開發者都以模塊化的方式處理該問題。在這篇文章中,咱們將探討前端開發人員目前使用的模塊化方案以及試圖解決的問題。javascript
模塊化可使你的代碼低耦合,功能模塊直接不相互影響。html
講完了JavaScript模塊化的好處,咱們來看下有哪些解決方案來實現JavaScript的模塊化。前端
var myRevealingModule = (function () { var privateVar = "Ben Cherry", publicVar = "Hey there!"; function privateFunction() { console.log( "Name:" + privateVar ); } function publicSetName( strName ) { privateVar = strName; } function publicGetName() { privateFunction(); } // Reveal public pointers to // private functions and properties return { setName: publicSetName, greeting: publicVar, getName: publicGetName }; })(); myRevealingModule.setName( "Paul Kinlan" );
經過這種構造,咱們經過使用函數有了本身的做用域或「閉包」。java
這種方法的好處在於,你能夠在函數內部使用局部變量,而不會意外覆蓋同名全局變量,但仍然可以訪問到全局變量。node
優勢:es6
缺點:編程
CommonJS是一個旨在定義一系列規範的項目,以幫助開發服務器端JavaScript應用程序。CommonJS團隊試圖解決的一個領域就是模塊。Node.js開發人員最初打算遵循CommonJS規範,但後來決定反對它。segmentfault
在 CommonJS 的規範中,每一個 JavaScript 文件就是一個獨立的模塊上下文(module context),在這個上下文中默認建立的屬性都是私有的。也就是說,在一個文件定義的變量(還包括函數和類),都是私有的,對其餘文件是不可見的。瀏覽器
須要注意的一點是,CommonJS以服務器優先的方式來同步載入模塊,假使咱們引入三個模塊的話,他們會一個個地被載入。服務器
// In circle.js const PI = Math.PI; exports.area = (r) => PI * r * r; exports.circumference = (r) => 2 * PI * r; // In some file const circle = require('./circle.js'); console.log( `The area of a circle of radius 4 is ${circle.area(4)}`);
Node.js的模塊系統經過library的方式對CommonJS的基礎上進行了模塊化實現。
在Node和CommonJS的模塊中,基本上有兩個與模塊系統交互的關鍵字:require和exports。
require是一個函數,可用於將接口從另外一個模塊導入當前範圍。傳遞給的參數require是模塊的id。在Node的實現中,它是node_modules目錄中模塊的名稱(或者,若是它不在該目錄中,則是它的路徑)。
exports是一個特殊的對象:放入它的任何東西都將做爲公共元素導出。
Node和CommonJS之間的一個獨特區別在於module.exports對象的形式。
在Node中,module.exports是導出的真正特殊對象,而exports它只是默認綁定到的變量module.exports。
另外一方面,CommonJS沒有任何module.exports對象。實際意義是,在Node中,沒法經過如下方式導出徹底預構造的對象module.exports:
// This won't work, replacing exports entirely breaks the binding to // modules.exports. exports = (width) => { return { area: () => width * width }; } // This works as expected. module.exports = (width) => { return { area: () => width * width }; }
優勢
require
能夠在任何地方調用:模塊能夠經過編程方式加載。缺點
AMD誕生於一羣對CommonJS的研究方向不滿的開發人員。事實上,AMD在開發早期就與CommonJS分道揚鑣,AMD和CommonJS之間的主要區別在於它支持異步模塊加載。
//Calling define with a dependency array and a factory function define(['dep1', 'dep2'], function (dep1, dep2) { //Define the module value by returning a value. return function () {}; }); // Or: define(function (require) { var dep1 = require('dep1'), dep2 = require('dep2'); return function () {}; });
經過使用JavaScript的傳統閉包來實現異步加載:
在請求的模塊加載完成時調用函數。模塊定義和導入模塊由同一個函數承載:定義模塊時,其依賴關係是明確的。所以,AMD加載器能夠在運行時具備項目的模塊依賴圖。所以能夠同時加載彼此不依賴的庫。這對於瀏覽器尤爲重要,由於啓動時間對於良好的用戶體驗相當重要。
優勢
缺點
除了異步加載之外,AMD的另外一個優勢是你能夠在模塊裏使用對象、函數、構造函數、字符串、JSON或者別的數據類型,而CommonJS只支持對象。
統一模塊定義(UMD:Universal Module Definition )就是將 AMD 和 CommonJS 合在一塊兒的一種嘗試,常見的作法是將CommonJS 語法包裹在兼容 AMD 的代碼中。
(function(define) { define(function () { return { sayHello: function () { console.log('hello'); } }; }); }( typeof module === 'object' && module.exports && typeof define !== 'function' ? function (factory) { module.exports = factory(); } : define ));
該模式的核心思想在於所謂的 IIFE(Immediately Invoked Function Expression),該函數會根據環境來判斷須要的參數類別
支持JavaScript標準化的ECMA團隊決定解決模塊問題, 兼容同步和異步操做模式。
//------ lib.js ------ export const sqrt = Math.sqrt; export function square(x) { return x * x; } export function diag(x, y) { return sqrt(square(x) + square(y)); } //------ main.js ------ import { square, diag } from 'lib'; console.log(square(11)); // 121 console.log(diag(4, 3)); // 5
ES6 模塊的設計思想是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都只能在運行時肯定這些東西。
因爲 ES6 模塊是編譯時加載,使得靜態分析成爲可能。有了它,就能進一步拓寬 JavaScript 的語法,好比引入宏(macro)和類型檢驗(type system)這些只能靠靜態分析實現的功能。除了靜態加載帶來的各類好處,ES6 模塊還有如下好處。
ES6 的模塊自動採用嚴格模式,無論有沒有在模塊頭部加上"use strict";。
嚴格模式主要有如下限制。
變量必須聲明後再使用 函數的參數不能有同名屬性,不然報錯 不能使用with語句 不能對只讀屬性賦值,不然報錯 不能使用前綴 0 表示八進制數,不然報錯 不能刪除不可刪除的屬性,不然報錯 不能刪除變量delete prop,會報錯,只能刪除屬性delete global[prop] eval不會在它的外層做用域引入變量 eval和arguments不能被從新賦值 arguments不會自動反映函數參數的變化 不能使用arguments.callee 不能使用arguments.caller 禁止this指向全局對象 不能使用fn.caller和fn.arguments獲取函數調用的堆棧 增長了保留字(好比protected、static和interface)
其中,尤爲須要注意this的限制。ES6 模塊之中,頂層的this指向undefined,即不該該在頂層代碼使用this。
export語法被用來建立JavaScript模塊。你能夠用它來導出對象(包括函數)和原始值(primitive values)。導出有兩種類型:named和default。
// named // lib.js export function sum(a, b) { return a + b; } export function substract(a, b) { return a - b; } function divide(a, b) { return a / b; } export { divide };
// default // dog.js export default class Dog { bark() { console.log('bark!'); } }
import語句用來導入其餘模塊。
整個導入
// index.js import * as lib from './lib.js'; console.log(lib.sum(1,2)); console.log(lib.substract(3,1)); console.log(lib.divide(6,3));
導入一個或多個named導出
// index.js import { sum, substract, divide } from './lib'; console.log(sum(1,2)); console.log(substract(3,1)); console.log(divide(6,3));
須要注意,相應的導入導出名字必須匹配。
導入一個default導出
// index.js import Dog from './dog.js'; const dog = new Dog(); dog.bark(); // 'bark!'
注意,defualt導出在導入時,能夠用任意的名字。因此咱們能夠這樣作:
import Cat from './dog.js'; const dog = new Cat(); dog.bark(); // 'bark!'
原文出處:https://www.cnblogs.com/jingh/p/10915466.html