JavaScript 模塊相關

在編寫稍大些的項目的時候,模塊化和組件化是當前 JS 的最佳解決方案。在NodeJS中,通常將代碼合理拆分到不一樣的JS文件中,每個文件就是一個模塊,而文件路徑就是模塊名。javascript

模塊化代碼在 nodejs 中有如下特性html

  1. Node.js是經過模塊的形式進行組織與調用的,在編寫每一個模塊時,都有requireexportsmodule 三個預先定義好的變量可供使用。
  2. 因此係統自帶了不少模塊
  3. 同時也提供了新模塊的擴展機制

1.1 require

require函數用於在當前模塊中加載和使用別的模塊,傳入一個模塊名(路徑),反回一個模塊導出對象。模塊名可以使用相對路徑(以./開頭),或者是絕對路徑(以/或C:之類的盤符開頭)。另外,模塊名中的.js擴展名能夠省略。如下是一個例子。前端

var foo1 = require('./foo');
var foo2 = require('./foo.js');
var foo3 = require('/home/user/foo');
var foo4 = require('/home/user/foo.js');
// foo1至foo4中保存的是同一個模塊的導出對象。
複製代碼

另外,可使用如下方式加載和使用一個JSON文件。java

var data = require('./data.json');
複製代碼

1.2 exports

exports對象是當前模塊的導出對象,用於導出模塊公有方法和屬性。別的模塊經過require函數使用當前模塊時獲得的就是當前模塊的exports對象。如下例子中導出了一個公有方法。node

exports.hello = function () {
    console.log('Hello World!');
};
複製代碼

1.3 module

經過module對象能夠訪問到當前模塊的一些相關信息,但最多的用途是替換當前模塊的導出對象。例如模塊導出對象默認是一個普通對象,若是想改爲一個函數的話,可使用如下方式。git

module.exports = function () {
    console.log('Hello World!');
};
複製代碼
  • 以上代碼中,模塊默認導出對象被替換爲一個函數。
  • 經過命令行參數傳遞給NodeJS以啓動程序的模塊被稱爲主模塊。主模塊負責調度組成整個程序的其它模塊完成工做。

官方實現require 的方式:github

function require(...) {
  var module = { exports: {} };
  ((module, exports) => {
    // Your module code here. In this example, define a function.
    function some_func() {};
    exports = some_func;
    // At this point, exports is no longer a shortcut to module.exports, and
    // this module will still export an empty default object.
    module.exports = some_func;
    // At this point, the module will now export some_func, instead of the
    // default object.
  })(module, module.exports);
  return module.exports;
}
複製代碼

若是 a.js require 了 b.js, 那麼在 b 中定義全局變量 t = 111 可否在 a 中直接打印出來?面試

① 每一個 .js 能獨立一個環境只是由於 node 幫你在外層包了一圈自執行, 因此你使用 t = 111 定義全局變量在其餘地方固然能拿到. 狀況以下:算法

// b.js
(function (exports, require, module, __filename, __dirname) {
  t = 111;
})();

// a.js
(function (exports, require, module, __filename, __dirname) {
  // ...
  console.log(t); // 111
})();
複製代碼

a.js 和 b.js 兩個文件互相 require 是否會死循環? 雙方是否能導出變量? 如何從設計上避免這種問題?編程

② 不會, 先執行的導出空對象, 經過導出工廠函數讓對方從函數去拿比較好避免. 模塊在導出的只是 var module = { exports: {} };中的 exports, 以從 a.js 啓動爲例, a.js 還沒執行完 exports 就是 {} 在 b.js 的開頭拿到的就是 {} 而已.

另外還有很是基礎和常見的問題, 好比 module.exports 和 exports 的區別這裏也能一併解決了 exports 只是 module.exports 的一個引用. 沒看懂能夠在細看我之前發的帖子.

再晉級一點, 衆所周知, node 的模塊機制是基於 CommonJS 規範的. 對於從前端轉 node 的同窗, 若是面試官想問的難一點會考驗關於 CommonJS 的一些問題. 好比比較 AMD, CMD, CommonJS 三者的區別, 包括詢問關於 node 中 require 的實現原理等.

1.4 module.exports 與 exports

  1. 默認exportsmodule.exports指向相同的對象的引用,而且此對象是一個空對象{}
  2. 對他們添加屬性,不會破壞他們的一致性
console.log(module.exports === exports);   // true
console.log(exports.a);  // undefined

// 修改exports
module.exports.a = 100;
console.log(module.exports === exports);  // true
console.log(exports);  // { a: 100 }

// 修改exports
exports.b = {
	a: 100
};
console.log(module.exports === exports); // true
console.log(exports);  // { a: 100, b: { a: 100 } }
複製代碼
  1. 對他們直接使用賦值號,則會破壞他們的引用關係
console.log(module.exports === exports); // true
module.exports = {c:100}; 
console.log(exports); // {}
console.log(module.exports); // {c:100}
console.log(module.exports === exports); // false

// 直接修改exports
console.log(module.exports === exports); // false
exports = {
	c:100
};
console.log(exports); // {c:100}
console.log(module.exports); // {}
console.log(module.exports === exports); // false
複製代碼
  1. 導出以module.exports爲準

1.4 系統自帶模塊

能夠經過 process.moduleLoadList 打印的 NativeModule 能夠查看到相關的模塊信息。主要系統包括:

在 V8.9.3中,主要的系統模塊包括: [ 'assert', 'buffer', 'console', 'dns', 'domain', 'events', 'fs', 'module', 'net', 'os', 'path', 'querystring', 'readline', 'repl', 'stream', 'string_decoder', 'timers', 'tty', 'url', 'util', 'vm' ]

其中最能表明node.js的最初思想的是net, 'events'這兩個模塊。

1.5 系統 API |穩定性:2 -- 穩定的|v8.1.1|

1.5.1 exports 和 module.exports

先看一個例子:

// module circle.js
const { PI } = Math;

exports.area = (r) => PI * r ** 2;

exports.circumference = (r) => 2 * PI * r;
複製代碼
  • circle.js 文件導出了 area()circumference() 兩個函數。
  • 經過在特殊的 exports 對象上指定額外的屬性,函數和對象能夠被添加到模塊的根部。
  • 模塊內的本地變量是私有的,由於模塊被 Node.js 包裝在一個函數中。 (PI 是私有的)

第二個例子:

// square.js
module.exports = class Square {
  constructor(width) {
    this.width = width;
  }

  area() {
    return this.width ** 2;
  }
};
// use square module
const Square  = require('./square.js');
const mySquare = new Square(2);
console.log(`mySquare 的面積是 ${mySquare.area()}`);
複製代碼
  • module.exports 屬性能夠被賦予一個新的值(例如函數或對象)。

1.5.2 主模塊

  • 當 Node.js 直接運行一個文件時,require.main 會被設爲它的 module。 這意味着能夠經過 require.main === module 來判斷一個文件是否被直接運行:

  • module 提供了一個 filename 屬性(一般等同於 __filename),因此能夠經過檢查 require.main.filename 來獲取當前應用程序的入口點。

1.5.3 包管理器的技巧 http://nodejs.cn/api/modules.html#modules_addenda_package_manager_tips

1.5.4 緩存

  • 模塊在第一次加載後會被緩存。 這也意味着(相似其餘緩存機制)若是每次調用 require('foo') 都解析到同一文件,則返回相同的對象。

  • 屢次調用 require(foo) 不會致使模塊的代碼被執行屢次。 這是一個重要的特性。 藉助它, 能夠返回「部分完成」的對象,從而容許加載依賴的依賴, 即便它們會致使循環依賴。

  • 若是想要屢次執行一個模塊,能夠導出一個函數,而後調用該函數。

模塊緩存的注意事項:

  • 模塊是基於其解析的文件名進行緩存的。 因爲調用模塊的位置的不一樣,模塊可能被解析成不一樣的文件名(好比從 node_modules 目錄加載),這樣就不能保證 require('foo') 總能返回徹底相同的對象。

  • 此外,在不區分大小寫的文件系統或操做系統中,被解析成不一樣的文件名能夠指向同一文件,但緩存仍然會將它們視爲不一樣的模塊,並屢次從新加載。 例如,require('./foo') 和 require('./FOO') 返回兩個不一樣的對象,而不會管 ./foo 和 ./FOO 是不是相同的文件。

  • 建議: 文件名稱按照必定規範進行編排,無大小寫轉換後相同文件名。

1.5.6 核心模塊

  • 核心模塊是 Nodejs將其編譯成二進制的模塊,便於更快速加載
  • 核心模塊存放在 Node.js 源代碼的 lib/ 目錄下。
  • require() 老是會優先加載核心模塊。 例如,require('http') 始終返回內置的 HTTP 模塊,即便有同名文件。

1.5.7 循環 在模塊之間的互相加載時,當 main.js 加載 a.js 時,a.js 又加載 b.js。 此時,b.js 會嘗試去加載 a.js, 這樣就形成了無限循環。

爲了防止無限的循環,Nodejs 提供了一個解決策略。

會返 a.jsexports 對象的未完成的副本給 b.js 模塊。 而後 b.js 完成加載,並將 exports 對象提供給 a.js 模塊。

DEMO:

//a.js:
console.log('a 開始');
exports.done = false;
const b = require('./b.js');
console.log('在 a 中,b.done = %j', b.done);
exports.done = true;
console.log('a 結束');

//b.js:
console.log('b 開始');
exports.done = false;
const a = require('./a.js');
console.log('在 b 中,a.done = %j', a.done);
exports.done = true;
console.log('b 結束');

//main.js:
console.log('main 開始');
const a = require('./a.js');
const b = require('./b.js');
console.log('在 main 中,a.done=%j,b.done=%j', a.done, b.done);

複製代碼

分析過程:

  • 關鍵詞: 會返 a.js 的 exports 對象的未完成的副本 給 b.js 。
  • 未完成程度 : 在出現循環引用的時候 返回副本。
  • 目的: 不阻礙主流程。
  • 執行順序:1 -> 2 -> 5~7 -> 8~16 -> 3 -> 4
    • 1~2 : 主流程
    • 5~7:按照require(a.js)順序執行,
    • 8~9:按照 b.js 數序執行,
    • 10: 出現循環:製造 當前狀態下 a.js 副本,狀態保留至副本創造時。
    • 11~16: 順序執行,而後 b.js 完成加載,並將 exports 對象提供給 a.js 模塊。
    • 3: 緩存讀取 b.js 但已經在 a.js 中執行過,這裏不執行。
    • 4: main.js 執行完畢。
  • 輸出結果:
$ node main.js
main 開始
a 開始
b 開始
在 b 中,a.done = false
b 結束
在 a 中,b.done = true
a 結束
在 main 中,a.done=true,b.done=true
複製代碼
  • 須要仔細的規劃, 以容許循環模塊依賴在應用程序內正常工做。

1.5.8 文件模塊(系統自帶的模塊)

  • 若是按確切的文件名沒有找到模塊,則 Node.js 會嘗試帶上.js.json.node 拓展名再加載。

    • .js 文件會被解析爲 JavaScript 文本文件
    • .json 文件會被解析爲 JSON 文本文件。
    • .node 文件會被解析爲經過 dlopen 加載的編譯後的插件模塊。
  • 以 '/' 爲前綴的模塊是文件的絕對路徑。 例如,require('/home/marco/foo.js') 會加載 /home/marco/foo.js 文件。

  • 以 './' 爲前綴的模塊是相對於調用 require() 的文件的。

  • 當沒有以 '/''./''../' 開頭來表示文件時,這個模塊必須是一個核心模塊或加載自 node_modules 目錄。

  • 若是給定的路徑不存在,則 require() 會拋出一個 code 屬性爲'MODULE_NOT_FOUND'的 Error。

1.5.9 目錄做爲模塊 若是把程序和依賴庫放在統一個文件夾下,提供一個單一的入口指向它。把目錄傳給 require() 做爲一個參數,即爲 目錄做爲模塊 引用。

  • 使用package.json指定main入口模塊:
{ 
  "name" : "some-library",
  "main" : "./lib/some-library.js" 
}
複製代碼

若是這是在 ./some-library 目錄中,則 require('./some-library') 會試圖加載 ./some-library/lib/some-library.js

  • package.json 指定,則 Nodejs 會試圖加載 index.jsindex.node
    • require('./some-library') : ./some-library/index.js./some-library/index.node

1.5.10 node_modules 目錄加載 傳入require()的路徑不是一個核心模塊,Nodejs 從父目錄開始,嘗試從父目錄的node_modules中加載模塊。 若是在'/home/ry/projects/foo.js' 文件裏調用了 require('bar.js'),則 Node.js 會按如下順序查找:

  • /home/ry/projects/node_modules/bar.js
  • /home/ry/node_modules/bar.js
  • /home/node_modules/bar.js
  • /node_modules/bar.js

經過在模塊名後包含一個路徑後綴,能夠請求特定的文件或分佈式的子模塊。 例如,require('example-module/path/to/file') 會把 path/to/file 解析成相對於 example-module 的位置。 後綴路徑一樣遵循模塊的解析語法。 1.5.11 從全局目錄加載 若是 NODE_PATH 環境變量被設爲一個以冒號分割的絕對路徑列表,則當在其餘地方找不到模塊時 Node.js 會搜索這些路徑。

**注意:**在 Windows 系統中,NODE_PATH 是以分號間隔的。

在當前的模塊解析算法運行以前,NODE_PATH 最初是建立來支持從不一樣路徑加載模塊的。

雖然 NODE_PATH 仍然被支持,但如今不太須要,由於 Node.js 生態系統已制定了一套存放依賴模塊的約定。 有時當人們沒意識到 NODE_PATH 必須被設置時,依賴 NODE_PATH 的部署會出現意料以外的行爲。 有時一個模塊的依賴會改變,致使在搜索 NODE_PATH 時加載了不一樣的版本(甚至不一樣的模塊)。

此外,Node.js 還會搜索如下位置:

  1. $HOME/.node_modules
  2. $HOME/.node_libraries
  3. $PREFIX/lib/node

其中 $HOME 是用戶的主目錄,$PREFIX 是 Node.js 裏配置的 node_prefix

這些主要是歷史緣由。

注意:強烈建議將全部的依賴放在本地的 node_modules 目錄。 這樣將會更快地加載,且更可靠。

1.5.12 [Module Scope] __dirname

  • <string>當前模塊的文件夾的名字
  • 等同於: path.dirname(__filename)的值

DEMO: 運行 /Users/demo/example.js

console.log(__dirname);
// Prints: /Users/demo
console.log(path.dirname(__filename));
// Prints: /Users/demo
複製代碼

1.5.12 [Module Scope] __filename

  • <string>當前模塊的文件名稱---解析後的絕對路徑。

DEMO: 運行/Users/demo/example.js

console.log(__filename);
// Prints: /Users/demo/example.js
console.log(__dirname);
// Prints: /Users/demo
複製代碼

給定兩個模塊: a 和 b, 其中 b 是 a 的一個依賴。

文件目錄結構以下:

  • /Users/mjr/app/a.js
  • /Users/mjr/app/node_modules/b/b.js

使用__filename===>

  • b.js 中對 __filename 的引用將會返回 /Users/mjr/app/node_modules/b/b.js
  • a.js 中對 __filename 的引用將會返回 /Users/mjr/app/a.js

1.5.13 [Module Scope] exports / module

  • exports: 這是一個對於 module.exports 的更簡短的引用形式。
  • module:對當前模塊的引用,

1.5.14 require()

用於引入模塊

  • require.cache: 被引入的模塊將被緩存在這個對象中。今後對象中刪除鍵值對將會致使下一次 require 從新加載被刪除的模塊。注意不能刪除 native addons(原生插件),由於它們的重載將會致使錯誤。

  • require.resolve(request[, options]) 使用內部的 require() 機制查詢模塊的位置, 此操做只返回解析後的文件名,不會加載該模塊。

    • request <string> 須要解析的模塊路徑。
    • options <Object>
      • paths <Array> 解析模塊的起點路徑。此參數存在時,將使用這些路徑而非默認解析路徑。 注意此數組中的每個路徑都被用做模塊解析算法的起點,意味着 node_modules 層級將從這裏開始查詢。
    • Returns: <string>
  • require.resolve.paths(request]) 返回一個數組,其中包含解析 request 過程當中被查詢的路徑。 若是 request 字符串指向核心模塊(例如 http 或 fs),則返回 null。

    • request: <string> 被查詢解析路徑的模塊的路徑。
    • 返回: <Array> | <null>

DEMO:

// modules
> require.resolve.paths('aaa')
[ '/Users/zhengao/repl/node_modules',
  '/Users/zhengao/node_modules',
  '/Users/node_modules',
  '/node_modules',
  '/Users/zhengao/.node_modules',
  '/Users/zhengao/.node_libraries',
  '/Users/zhengao/.nvm/versions/node/v8.9.3/lib/node',
  '/Users/zhengao/.node_modules',
  '/Users/zhengao/.node_libraries',
  '/Users/zhengao/.nvm/versions/node/v8.9.3/lib/node' ]
 // 核心模塊
 > require.resolve.paths('http')
null
複製代碼

1.5.15 module對象

  • <Object>
  • 在每一個模塊中,module 的自由變量是一個指向表示當前模塊的對象的引用。 爲了方便,module.exports 也能夠經過全局模塊的 exports 對象訪問。
  • module 不是全局的,而是每一個模塊本地的。只不過每一個模塊都有一個 module 對象而已。
> module
Module {
  id: '<repl>',
  exports: {},
  parent: undefined,
  filename: null,
  loaded: false,
  children: [],
  paths:  [ 
	 '/Users/zhengao/repl/node_modules',
     '/Users/zhengao/node_modules',
     '/Users/node_modules',
     '/node_modules',
     '/Users/zhengao/.node_modules',
     '/Users/zhengao/.node_libraries',
     '/Users/zhengao/.nvm/versions/node/v8.9.3/lib/node' 
  ]
}
複製代碼
  • module.children:
    • <Array>
    • 被該模塊引用的模塊對象。
  • module.exports:
    • <Object>
    • module.exports 對象是由模塊系統建立的。許多人但願他們的模塊成爲某個類的實例。 爲了實現這個,須要將指望導出的對象賦值給 module.exports。
    • **注意,**將指望的對象賦值給 exports 會簡單地從新綁定本地 exports 變量,這可能不是指望的。

DEMO:

// 許多人但願他們的模塊成爲某個類的實例, 須要將指望導出的對象賦值給 module.exports
//a.js
const EventEmitter = require('events');
module.exports = new EventEmitter();
// 處理一些工做,並在一段時間後從模塊自身觸發 'ready' 事件。
setTimeout(() => {
  module.exports.emit('ready');
}, 1000);
// 而後,在另外一個文件中能夠這麼作:
// b.js
const a = require('./a.js');
a.on('ready', () => {
  console.log('模塊 a 已準備好');
});

複製代碼

*DEMO: *

// 注意,對 module.exports 的賦值必須當即完成。 不能在任何回調中完成。不然無效
// x.js:
setTimeout(() => {
  module.exports = { a: 'hello' };
}, 0);

// y.js:
const x = require('./x');
console.log(x.a);
複製代碼
  • exports快捷方式
    • exports 變量是在模塊的文件級別做用域內有效的,它在模塊被執行前被賦予 module.exports 的值。
    • 它有一個快捷方式,以便 module.exports.f = ...能夠被更簡潔地寫成 exports.f = ...
    • 注意,就像任何變量,若是一個新的值被賦值給 exports,它就再也不綁定到 module.exports:
module.exports.hello = true; // 從對模塊的引用中導出
exports = { hello: false };  // 不導出,只在模塊內有效

//當 module.exports 屬性被一個新的對象徹底替代時,也會從新賦值 exports,例如:

module.exports = exports = function Constructor() {
  // ... 及其餘
};

//爲了解釋這個行爲,想象對 require() 的假設實現,它跟 require() 的實際實現至關相似:
function require(/* ... */) {
  const module = { exports: {} };
  ((module, exports) => {
    // 模塊代碼在這。在這個例子中,定義了一個函數。
    function someFunc() {}
    exports = someFunc;
    // 此時,exports 再也不是一個 module.exports 的快捷方式,
    // 且這個模塊依然導出一個空的默認對象。
    module.exports = someFunc;
    // 此時,該模塊導出 someFunc,而不是默認對象。
  })(module, module.exports);
  return module.exports;
}` 複製代碼
  • module.filename : 模塊的徹底解析後的文件名。
  • module.id: <string>模塊的標識符。 一般是徹底解析後的文件名。
  • module.loaded : <boolean>模塊是否已經加載完成,或正在加載中。
  • module.parent: <Object 模塊對象>最早引用該模塊的模塊。
  • module.paths<String []>模塊的搜索路徑。
  • module.require(id):
    • id: <string>
    • 返回: <Object> 已解析的模塊的 module.exports
    • module.require 方法提供了一種相似 require() 從原始模塊被調用的加載模塊的方式。
  • module.builtinModules:由Node.js提供的全部模塊的名稱列表。能夠用來驗證模塊是否被第三方模塊維護。

1.6 Q&A

Q1: 比較AMD,CMD和 CommonJS 三者區別 A:

背景: 網頁愈來愈像桌面程序,須要一個團隊分工協做、進度管理、單元測試等等......開發者不得不使用軟件工程的方法,管理網頁的業務邏輯。由於有了模塊,咱們就能夠更方便地使用別人的代碼,想要什麼功能,就加載什麼模塊。

  • CommonJS:

CommonJS規範是誕生比較早的。NodeJS就採用了CommonJS。CommonJS 是一種同步的模塊化規範,是這樣加載模塊:

var clock = require('clock');
clock.start();
複製代碼

這種寫法適合服務端,由於在服務器讀取模塊都是在本地磁盤,加載速度很快。可是,對於瀏覽器,這倒是一個大問題,由於模塊都放在服務器端,等待時間取決於網速的快慢,可能要等很長時間,瀏覽器處於"假死"狀態。

所以,瀏覽器端的模塊,不能採用"同步加載"(synchronous),只能採用"異步加載"(asynchronous)。

這就是AMD規範誕生的背景。好比上面的例子中clock的調用必須等待clock.js請求成功,加載完畢。

  • AMD:

AMD,即 (Asynchronous Module Definition),這種規範是異步的加載模塊,requireJs應用了這一規範。先定義全部依賴,而後在加載完成後的回調函數中執行:

require([module], callback);

第一個參數[module],是一個數組,裏面的成員就是要加載的模塊;第二個參數callback,則是加載成功以後的回調函數。若是將前面的代碼改寫成AMD形式,就是下面這樣:

require(['math'], function (math) {
	math.add(2, 3);
});
複製代碼

math.add()與math模塊加載不是同步的,瀏覽器不會發生假死。因此很顯然,AMD比較適合瀏覽器環境。

目前,主要有兩個Javascript庫實現了AMD規範:require.jscurl.js

AMD雖然實現了異步加載,可是開始就把全部依賴寫出來是不符合書寫的邏輯順序的,能不能像commonJS那樣用的時候再require,並且還支持異步加載後再執行呢?

  • CMD:

CMD (Common Module Definition), 是seajs推崇的規範,CMD則是依賴就近,用的時候再require。它寫起來是這樣的:

define(function(require, exports, module) {
   var clock = require('clock');
   clock.start();
});
複製代碼

AMD和CMD最大的區別是對依賴模塊的執行時機處理不一樣,而不是加載的時機或者方式不一樣,兩者皆爲異步加載模塊

AMD依賴前置,js能夠方便知道依賴模塊是誰,當即加載;

而CMD就近依賴,須要使用把模塊變爲字符串解析一遍才知道依賴了那些模塊,這也是不少人詬病CMD的一點,犧牲性能來帶來開發的便利性,實際上解析模塊用的時間短到能夠忽略。


Q2:Node.js 中 require()的實現

A:


Q3: 何時使用 exports ,何時使用 module.exports

A:用一句話來講明就是,require方能看到的只有module.exports這個對象,它是看不到exports對象的,而咱們在編寫模塊時用到的exports對象實際上只是對module.exports的引用。

var module = {
    exports:{
        name:"我是module的exports屬性"
    }
};
//exports是對module.exports的引用,也就是exports如今指向的內存地址和module.exports指向的內存地址是同樣的
var exports = module.exports;  

console.log(module.exports);    // { name: '我是module的exports屬性' }
console.log(exports);   // { name: '我是module的exports屬性' }

exports.name = "我想改一下名字";

console.log(module.exports);    // { name: '我想改一下名字' }
console.log(exports);   // { name: '我想改一下名字' }
//看到沒,引用的結果就是a和b都操做同一內存地址下的數據


//這個時候我在某個文件定義了一個想導出的模塊
var Circle = {
    name:"我是一個圓",
    func:function(x){
        return x*x*3.14;
    }
};

exports = Circle;  // 看清楚了,Circle這個Object在內存中指向了新的地址,因此exports也指向了這個新的地址,和原來的地址沒有半毛錢關係了

console.log(module.exports);    // { name: '我想改一下名字' }
console.log(exports);   // { name: '我是一個圓', func: [Function] }

複製代碼
  • 直接更改引用: exportsmodule.exports 指向的是同一個引用
  • 直接賦值: 賦值給exports 內部做用域使用,賦值給 module.exports 能夠被require() 引用。

1.7 模塊化的基本要求

  • 高內聚
  • 低耦合
  • 邏輯清晰正確
  • 要有輸出
  • 高扇入,低扇出
  • 減小冗餘(相同類型不要超過三遍)

1.8 模塊化的代碼規範

在 Node.js 中使用 CommonJS 使用模塊規範

相關文章
相關標籤/搜索