帶着下面的問題去讀讀代碼吧!javascript
node
裏面的文件就是模塊?js
文件,是如何執行的?exports
module.exports
的關係?js
裏面的對象之間的引用關係?在node
裏面,js
代碼在被引入過去都會被包裝成以下的函數,是否是咱們只須要再咱們的require
函數裏面去調用這個函數(固然下面的那個函數須要咱們本身讀取到文件內容,而後組裝成下面的函數形式),解析函數字符串爲可執行的js,傳入引用的參數。module.exports
就會被賦值。再返回module.exports
,就獲得了須要的模塊裏面的內容了前端
// (function(exports,module,require){ this == module.exports // true module.exports = 'hello'; // })
module.exports
與 exports
exports = module.exports exports.a = 'xxx' // 一個引用地址,module.exports也會被賦值 exports = 'xxx' // 這個時候exports的引用地址就錯誤了。就獲取不到模塊的內容了
但咱們傳遞給函數的參數是一個引用類型數據的時候。當函數執行的時候,會先執行參數賦值的階段,引用類型數據的話,賦值的就是一個引用地址。就會出現以下的狀況java
var test = {} function fn(a){ a.name = 'xxx' } fn(test) console.log(test) // {name:'xxx'}
const fs = require("fs"); const path = require("path"); const vm = require("vm"); function Module(filePath) { this.id = filePath; this.exports = {}; } Module._cache = {}; Module.fnStr = ["(function(module,exports,req,__fileName,__dirname){\n", "})"]; Module.extensions = { ".js": function(module) { const content = fs.readFileSync(module.id, "utf8"); const fnString = Module.fnStr[0] + content + Module.fnStr[1]; const fn = vm.runInThisContext(fnString); // 文件就是模塊 exports = module.exports exports其實就是指向的 module.exports // 函數執行後 會給傳遞進去的module.exports賦值。module又是一個對象,函數參數的引用,就給下面實例化的 module上賦值了。就可以獲取到文件的內容了 fn.call( module.exports, module, module.exports, req, module.id, path.dirname(module.id) ); }, ".json": function(module) { const jsonString = fs.readFileSync(module.id, "utf8"); module.exports = JSON.parse(jsonString); } }; Module.resolveFileName = function(filePath) { let absolutePath = path.resolve(__dirname, filePath); let flag = fs.existsSync(absolutePath); // 判斷文件是否存在 let current = absolutePath; if (!flag) { let keys = Object.keys(Module.extensions); for (let i = 0; i < keys.length; i++) { let combinePath = absolutePath + keys[i]; let flag = fs.existsSync(combinePath); // 增長文件後綴後再判斷文件是否存在 if (flag) { current = combinePath; break; } else { current = null; } } } if (!current) { throw new Error(`當前文件不存在${current}`); } return current; }; Module.prototype.load = function() { // this.id 就是文件路徑 let extName = path.extname(this.id); Module.extensions[extName](this); }; function req(filePath) { let absolutePath = Module.resolveFileName(filePath); // 獲取到文件的絕對路徑 支持了 動態匹配.js .json後綴 let module = new Module(absolutePath); if (Module._cache[absolutePath]) { return Module._cache[absolutePath].exports; } // 值是一個引用地址。真正有值的時候是再執行完module.load()後 Module._cache[absolutePath] = module; module.load(); // 讀取文件 return module.exports; }
本人前端菜鳥一枚,歡迎閱讀並指出錯誤!