前言
作為一個接口API測試工程師,你是否曾經(jīng)有過疑惑,有些項目在模仿(fu zhi zhan tie)原有的代碼時,有時候是通過export 將模塊暴露出去,有時候是通過export default,有些包可以通過import引用,有些是require,那么,來總結一下這些區(qū)別
首先了解一些基本的概念
AMD(RequireJS)
- AMD一開始是CommonJS規(guī)范中的一個草案,全稱是Asynchronous Module Definition,即異步模塊加載機制。后來由該草案的作者以RequireJS實現(xiàn)了AMD規(guī)范,所以一般說AMD也是指RequireJS。
- 特點:
- 對于依賴的模塊,AMD推崇依賴前置,提前執(zhí)行。也就是說,在define方法里傳入的依賴模塊(數(shù)組),會在一開始就下載并執(zhí)行。
- 模塊的加載不影響它后面語句的運行。所有依賴這個模塊的語句,都定義在一個回調(diào)函數(shù)中,等到加載完成之后,這個回調(diào)函數(shù)才會運行。
- 使用:通過
define來定義一個模塊,使用require可以導入定義的模塊。
// a.js
/*
* define(id?, dependencies?, factory);
* id:可選參數(shù),用來定義模塊的標識,如果沒有提供該參數(shù),腳本文件名(去掉拓展名)
* dependencies:是一個當前模塊依賴的模塊名稱數(shù)組
* factory:工廠方法,模塊初始化要執(zhí)行的函數(shù)或?qū)ο?。如果為函?shù),它應該只被執(zhí)行一次。如果是對象,此對象應該為模塊的輸出值 在頁面上使用require函數(shù)加載模塊
*/
define(function(){
return 1;
})
// b.js
/*
*數(shù)組中聲明需要加載的模塊,可以是模塊名、js文件路徑
* require([dependencies], callback);
* [dependencies]: 是一個數(shù)組,表示所依賴的模塊
* callback: 是一個回調(diào)函數(shù),當前面指定的模塊都加載成功后,它將被調(diào)用。加載的模塊會以參數(shù)形式傳入該函數(shù),從而在回調(diào)函數(shù)內(nèi)部就可以使用這些模塊
*/
require(['a'], function(a){
console.log(a);// 1
});
CMD
- CMD是SeaJS在推廣過程中生產(chǎn)的對模塊定義的規(guī)范,在Web瀏覽器端的模塊加載器中,SeaJS與RequireJS并稱,SeaJS作者為阿里的玉伯。
- 特點:對于依賴的模塊,CMD推崇依賴就近,延遲執(zhí)行。也就是說,只有到
require時依賴模塊才執(zhí)行。 - 使用:
// a.js
/*
* define 接受 factory 參數(shù),factory 可以是一個函數(shù),也可以是一個對象或字符串
* define(id?, deps?, factory)
* id 表示模塊標識
* 數(shù)組 deps 是模塊依賴
* factory 為對象、字符串時,表示模塊的接口就是該對象、字符串。
*/
define(function(require, exports, module) {
var $ = require('jquery');
exports.setColor = function() {
$('body').css('color','#333');
};
});
//b.js
//數(shù)組中聲明需要加載的模塊,可以是模塊名、js文件路徑
seajs.use(['a'], function(a) {
$('#el').click(a.setColor);
});
CommonJS
- CommonJS規(guī)范為CommonJS小組所提出,目的是彌補JavaScript在服務器端缺少模塊化機制,NodeJS、webpack都是基于該規(guī)范來實現(xiàn)的。
在 ES6 之前,ECMAScript 并沒有提供代碼組織的方式,那時候通常是基于 IIFE 來實現(xiàn)“模塊化”,隨著 JavaScript 在前端大規(guī)模的應用,以及服務端 Javascript 的推動,原先瀏覽器端的模塊規(guī)范不利于大規(guī)模應用。于是早期便有了 CommonJS 規(guī)范,其目標是為了定義模塊,提供通用的模塊組織方式。 - 特點:
- 所有代碼都運行在模塊作用域,不會污染全局作用域;
- 模塊是同步加載的,即只有加載完成,才能執(zhí)行后面的操作;
- 模塊在首次執(zhí)行后就會緩存,再次加載只返回緩存結果,如果想要再次執(zhí)行,可清除緩存;
- require返回的值是被輸出的值的拷貝,模塊內(nèi)部的變化也不會影響這個值。
- 使用:
//a.js
module.exports = function () {
console.log("hello world")
}
//b.js
var a = require('./a');
a();//"hello world"
//或者
//a2.js
exports.num = 1;
exports.obj = {xx: 2};
//b2.js
var a2 = require('./a2');
console.log(a2);//{ num: 1, obj: { xx: 2 } }
ES6 Module
- ES6 Module是ES6中規(guī)定的模塊體系,相比上面提到的規(guī)范, ES6 Module有更多的優(yōu)勢,有望成為瀏覽器和服務器通用的模塊解決方案。
- 特點(對比CommonJS):
- CommonJS模塊是運行時加載,ES6 Module是編譯時輸出接口;
- CommonJS加載的是整個模塊,將所有的接口全部加載進來,ES6 Module可以單獨加載其中的某個接口;
- CommonJS輸出是值的拷貝,ES6 Module輸出的是值的引用,被輸出模塊的內(nèi)部的改變會影響引用的改變;
- CommonJS this指向當前模塊,ES6 Module this指向undefined;
- 目前瀏覽器對ES6 Module兼容還不太好,我們平時在webpack中使用的export/import,會經(jīng)過babel轉換為CommonJS規(guī)范。
- 使用:
//a.js
var name = 'lin';
var age = 13;
var job = 'ninja';
export { name, age, job};
//b.js
import { name, age, job} from './a.js';
console.log(name, age, job);// lin 13 ninja
//或者
//a2.js
export default function () {
console.log('default ');
}
//b2.js
import customName from './a2.js';
customName(); // 'default'
Node.js模塊里exports與module.exports的區(qū)別
其實require導出的內(nèi)容是module.exports的指向的內(nèi)存塊內(nèi)容,并不是exports的。 簡而言之,區(qū)分他們之間的區(qū)別就是 exports 只是 module.exports的引用,輔助后者添加內(nèi)容用的。
為了避免糊涂,盡量都用 module.exports 導出,然后用require導入。
var module = {
exports:{
name:"我是module的exports屬性"
}
};
var exports = module.exports; //exports是對module.exports的引用,也就是exports現(xiàn)在指向的內(nèi)存地址和module.exports指向的內(nèi)存地址是一樣的
console.log(module.exports); // { name: '我是module的exports屬性' }
console.log(exports); // { name: '我是module的exports屬性' }
exports.name = "我想改一下名字";console.log(module.exports); // { name: '我想改一下名字' }
console.log(exports); // { name: '我想改一下名字' }
//看到?jīng)],引用的結果就是a和b都操作同一內(nèi)存地址下的數(shù)據(jù)
//這個時候我在某個文件定義了一個想導出的模塊
var Circle = {
name:"我是一個圓",
func:function(x){
return x*x*3.14;
}
};
exports = Circle; // 看清楚了,Circle這個Object在內(nèi)存中指向了新的地址,所以exports也指向了這個新的地址,和原來的地址沒有半毛錢關系了
console.log(module.exports); // { name: '我想改一下名字' }
console.log(exports); // { name: '我是一個圓', func: [Function] }
ES中的模塊重的export 和 export default的區(qū)別
- export與export default均可用于導出常量、函數(shù)、文件、模塊等
- 在一個文件或模塊中,export、import可以有多個,export default僅有一個
- 通過export方式導出,在導入時要加{ },export default則不需要
- export能直接導出變量表達式,export default不行。