Implementing Simple Version of commonjs Loading
Read the code with the following questions.
- Is the file in node module?
- How is the js file read executed?
- exports module.exports relationship?
- Reference relationships between objects in js?
How do you understand that a file is a module?
In node, JS code is wrapped into the following function when it is introduced. Does it mean that we only need to call this function in our require function (of course, the following function needs to read the file content by ourselves, then assemble it into the following function form), parse the function string as executable js, and pass in the reference parameters? module.exports will be assigned. Return to module.exports and you get the content of the module you need.
// (function(exports,module,require){ this == module.exports // true module.exports = 'hello'; // })
module.exports and exports
exports = module.exports exports.a = 'xxx' // A reference address, module.exports, is also assigned exports = 'xxx' // At this point, the quotation address of exports is wrong. You can't get the content of the module.
Reference to parameters
But the parameter we pass to the function is a reference to type data. When a function is executed, the parameter assignment stage is executed first. If the type data is referenced, the assignment is a reference address. The following will happen
var test = {} function fn(a){ a.name = 'xxx' } fn(test) console.log(test) // {name:'xxx'}
The code is implemented as follows
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); // The file is the module exports = module. export exports, which is actually the module to which you point. // The module.exports passed in are assigned after the function is executed. Module is another object, and the reference of function parameters assigns values to the module instantiated below. You can get the contents of the file. 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); // Determine whether a file exists 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); // Add file suffix before judging whether file exists. if (flag) { current = combinePath; break; } else { current = null; } } } if (!current) { throw new Error(`Current file does not exist ${current}`); } return current; }; Module.prototype.load = function() { // this.id is the file path let extName = path.extname(this.id); Module.extensions[extName](this); }; function req(filePath) { let absolutePath = Module.resolveFileName(filePath); // The absolute path to the file supports dynamic matching. js. JSON suffix let module = new Module(absolutePath); if (Module._cache[absolutePath]) { return Module._cache[absolutePath].exports; } // The value is a reference address. The real value comes when module.load() is executed Module._cache[absolutePath] = module; module.load(); // read file return module.exports; }
I am a rookie at the front, welcome to read and point out mistakes!