const vm = require('vm')
const fs = require('fs')
const path = require('path')
function Module(id){
this.id = id
this.exports = {}
}
Module.prototype.load = function() {
let ext = path.extname(this.id)
Module._extensions[ext](this)
}
Module.wrap = function(script) {
let wrapper = ['(function(exports,require,module,dirname,filename){', '})']
return wrapper[0] + script + wrapper[1]
}
Module._extensions = {
'.js': function(module) {
let script = fs.readFileSync(module.id, 'utf8')
let wrapperStr = Module.wrap(script)
let fn = vm.runInThisContext(wrapperStr)
fn.call(module.exports, module.exports, req, module, path.dirname(module.id), module.id)
},
'.json': function(module) {
let data = fs.readFileSync(module.id, 'utf8')
module.exports = data
}
}
Module._resolveFilename = function(filename) {
let file = path.resolve(__dirname, filename)
let exist = fs.existsSync(file)
if(exist) return file
let exts = Object.keys(Module._extensions)
for (let ext of exts) {
let fileStr = file + ext
if(fs.existsSync(fileStr)){
return fileStr
}
}
}
Module.cache = {}
function req(filename){
let filepath = Module._resolveFilename(filename)
if (Module.cash[filename]) { // 缓存机制
return Module.cache[filename].exports
}
let module = new Module(filepath)
Module.cash = {[module.id]: module}
module.load()
return module.exports
}
const a = req('./a')
console.log(a)
/**
* commonJS原理:
* require是同步加载,主要是通过函数隔离
* 如果是json,通过fs.readFileSync获取内容后,赋值给module.exports;
* 如果是js把每个文件转成函数字符串去执行,每次执行都传入一个module类,有两个重要的属性,id和exports, id是文件的绝对路径,用于通过fs获取内容,另一个作用是做缓存对象的key
* exports初始值是{},文件的this是module.exports,即{}
* 所以有三种写法(引用数据类型):
* this.a = 1
* module.exports.a = 1
* exports.a = 1
* 如果是非引用数据类型,则以module.exports为主
*/