前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >node乞丐版require方法

node乞丐版require方法

作者头像
wade
发布2020-04-23 16:13:21
6300
发布2020-04-23 16:13:21
举报
文章被收录于专栏:coding个人笔记

在node环境中,每一个文件都是一个模块,解决了命名冲突的问题,node会默认给js加上一个函数,比如一个js文件打印this:

代码语言:javascript
复制
console.log(this);//{}

并不是像浏览器一样,this是window(node没有window,只有global),而是一个空对象,再打印arguments:

代码语言:javascript
复制
  '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方法引入的时候相当于一个自执行函数:

代码语言:javascript
复制
(function (exports, require, module, __filename, __dirname) {
})

且我们可以实现一个简易的require方法:

1、_resolveFilename处理路径,判断存在和转换成绝对路径

2、创建module实例new Module

3、调用实例方法load加载文件

4、通过_extensions处理不同文件

5、给文件加一个函数并自执行

6、处理多次调用

7、返回module.exports

直接上代码:

代码语言:javascript
复制
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的方法上千行,我这只是学习一下大概原理,可以拷贝代码运行,然后理解理解。

(完)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 coding个人笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档