node乞丐版require方法

在node环境中,每一个文件都是一个模块,解决了命名冲突的问题,node会默认给js加上一个函数,比如一个js文件打印this: console.log(this);//{} 并不是像浏览器一样,this是window(node没有window,只有global),而是一个空对象,再打印arguments: '0': {}, '1': { [Function: require] '2':Module '3': 'E:\\mydata\\project\\bootstrap\\html\\coding\\nodenodule\\a.js', '4': 'E:\\mydata\\project\\bootstrap\\html\\coding\\nodenodule' 内容有点多,可以自行打印看看,但是可以看到,是五个参数,通过一个大佬的对node的调试和过程分析,确定require方法引入的时候相当于一个自执行函数: (function (exports, require, module, __filename, __dirname) { }) 且我们可以实现一个简易的require方法: 1、_resolveFilename处理路径,判断存在和转换成绝对路径 2、创建module实例new Module 3、调用实例方法load加载文件 4、通过_extensions处理不同文件 5、给文件加一个函数并自执行 6、处理多次调用 7、返回module.exports 直接上代码: const fs = require('fs'); const path = require('path'); const vm = require('vm');//虚拟机,可以到node官网文档了解 //自定义的require方法 function myrequire(filename) { //处理路径 filename = Module._resolveFilename(filename); //判断是否有缓存 if(Module._cache[filename]){ return Module._cache[filename].exports; } //初始化实例 let module = new Module(filename); //进行加载 module.load(); Module._cache[filename] = module; //返回内容 return module.exports; } //缓存 Module._cache = {} //构造函数,主要内容id(也就是路径做为id)和一个空对象 function Module(id) { this.id = id; this.exports = exports; } //处理路径方法 Module._resolveFilename = function (filename) { //生成绝对路径 let absPath = path.resolve(__dirname, filename); //fs方法,判断是否存在文件 if(fs.existsSync(absPath)){ return absPath; }else{ //引入有可能是require('./a'),没有后缀,手动拼接上判断 let keys = Object.keys(Module._extensions); for(let i = 0; i< keys.length; i++){ let path = absPath + keys[i]; if(fs.existsSync(path)){ //不加后缀,寻找到第一个直接返回,如果同名不同后缀不往下走 return path; } }; //如果都没有直接报错不存在 throw new Error('module is not exist'); } } //不同文件处理方法 Module._extensions = { '.js'(module){ //同步读取文件 let content = fs.readFileSync(module.id, 'utf8'); //构造成一个函数 content = Module.wrapper[0] + content + Module.wrapper[1]; //使用runInThisContext变成不依赖上下文环境方法 let fn = vm.runInThisContext(content); let exports = module.exports; let dirname = path.dirname(module.id); //调用方法,改变this fn.call(exports, exports, myrequire, module, module.id, dirname); }, '.json'(module){ //直接可以返回 let content = fs.readFileSync(module.id, 'utf8'); module.exports = content; } } //js内容包裹成一个函数使用 Module.wrapper = [ '(function(exports, require, module, __firname, __dirname){', '})' ] //加载方法 Module.prototype.load = function () { //获取后缀,调用不同后缀方法 let extname = path.extname(this.id); Module._extensions[extname](this); } let a = myrequire('./a'); let j = myrequire('./j'); console.log(a); console.log(j); node的方法上千行,我这只是学习一下大概原理,可以拷贝代码运行,然后理解理解。 image

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):

昵称:
邮箱:
内容: