Node.js design pattern : module

Node.js design pattern一書中對Node的Module模塊機制這一塊,我以爲講的挺透徹和易懂,這裏根據本身理解作下總結。本文轉發自本人githubjavascript

loadModule

自定義一個簡單的模塊加載方法loadModule,基本思路跟nodejs一致,將加載的模塊內容包裹在一個函數裏面實現變量的隔離,保證模塊內的變量都是私有的。java

function loadModule(filename, module, require) {
		const wrappedSrc = `(function(module, exports, require) { ${fs.readFileSync(filename, 'utf8')} })(module, module.exports, require);`;
	
		eval(wrappedSrc);
	}
複製代碼

這個例子經過evalwrappedSrc進行計算,即經過eval函數處理該字符串腳本。由於這裏要把(function(module, exports, require) { 這串東西和 fs.readFileSync(filename, 'utf8')加載的模塊內容合併在一塊兒做爲新的整合代碼再運行,因此必須藉助eval函數。node

做爲對比,在nodejs源碼中, wrap是這樣實現的git

Module.wrap = function(script) {
  		return Module.wrapper[0] + script + Module.wrapper[1];
	};

	Module.wrapper = [
	  '(function (exports, require, module, __filename, __dirname) { ',
	  '\n});'
	];
複製代碼

最終對該warpper的解析實如今Module.prototype._compile方法中,其中用到了vm模塊對wrapper腳本進行處理。vm實現的功能與eval函數相似,但比eval函數更強大。github

模塊引用require()

對模塊的引用咱們經過require(..)函數進行引用,如var http = require('http'),該方法簡單實現以下:緩存

const require = (moduleName) => {
		console.log(`Require invoked for module: ${moduleName}`);
		const id = require.resolve(moduleName);
		if (require.cache[id]) { return require.cache[id].exports; }
	
		// 1.module metadata
		const module = {
			exports: {},
			id: id
		}
	
		// 2.require.cache
		require.cache[id] = module;
	
		// 3.load the module
		loadModule(id, module, require);
	
		// 4.return exported variables
		return module.exports;
	}
	
	require.cache = {};
	require.resolve = (moduleName) => {
		/* resolve a full module id from the moduleName */
	}
複製代碼
  1. 定義了一個module對象用來保存經過loadModule方法中加載模塊中暴露出的接口。
  2. 將第一次加載的模塊保存在內部緩存中。即第二次調用require(..)時不會再調用loadModule方法,直接從cache中返回。
  3. 經過loadModule方法經過模塊路徑加載模塊內容。
  4. 返回模塊中暴露的接口以供調用。

能夠經過下圖更加直觀的瞭解其中的關係。app

nodejs-module

module.exports vs exports函數

經過上述代碼和圖示可知,咱們寫的模塊中的exports實際上是對module.exports的引用。 所以咱們能夠經過exports添加屬性來給module.exports引用的對象添加屬性。ui

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

但若是給exports從新賦值,則會失去module.exports的引用spa

exports = () => { console.log('Hello') };
複製代碼

此時exports !== module.exports,意味着經過exports暴露的接口是無效的,沒有添加到module metadata中的exports中。

相關文章
相關標籤/搜索