在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的方法上千行,我这只是学习一下大概原理,可以拷贝代码运行,然后理解理解。
网友评论