美文网首页
node的require方法实现原理

node的require方法实现原理

作者: 简公孙策 | 来源:发表于2020-02-13 16:53 被阅读0次
const path = require('path');
const fs = require('fs');
const vm = require('vm');

class ZLModule {
    constructor(id) {
        this.id = id;//保存当前模块的绝对路径
        this.exports = {};
    }
}
ZLModule._cache = {};//存储已经加载过的module对象
ZLModule._extensions = {
    '.json': function(module) {
        let json = fs.readFileSync(module.id);
        module.exports = JSON.parse(json);
    },
    '.js': function (module) {
        //将js文件包裹起来,包裹成一个方法
        let script = fs.readFileSync(module.id);
        script = ZLModule.wrapper[0] + '\n' + script + ZLModule.wrapper[1];
        //将字符串转化成方法
        fn = vm.runInThisContext(script);
        fn.call(module.exports,module.exports,require,module);
    }
}
ZLModule.wrapper = ['(function(exports, require, module, __filename, __dirname) {','\n})'];

function ZLRequire(filePath) {
    //1、转化成绝对路径
    let absPath = path.join(__dirname, filePath);
    //2、根据路径从缓存中获取模块,如果没有则新建模块对象(新对象需要加载执行);并最终返回模块对象的exports对象。
    let cacheModule = ZLModule._cache[absPath];
    if(cacheModule) {
        return cacheModule.exports;
    }else {
        let module = new ZLModule(absPath);
        ZLModule._cache[absPath] = module;
        tryModuleLoad(module);//处理模块,主要是处理module.exports对象
        return module.exports; 
    }
}

function tryModuleLoad(module) {
    // 取出模块后缀,根据不同后缀名调用不同的加载方法
    let extName = path.extname(module.id);
    let fn = ZLModule._extensions[extName];
    // ZLModule._extensions[extName](module);
    fn(module);

}

const aModule = ZLRequire('a.js');
一个类Module: 用来定义引入文件(模块的实例,含有两个实例属性,id-保存文件绝对路径和exports-保存文件模块导出对象)

Molude类上保存了两个公共属性:_cache对象(用来存储已经加载过的js模块,通过路径识别);_extensions对象(用来保存不同类型文件的处理方法,根据文件路径后缀名区别)

两个方法: require和tryModuleLoad

require: 用来引入文件(文件路径),直接返回已经加载过的module对象的exports对象;或对于新引入文件创建module实例(并调用tryModuleLoad方法处理文件,得到最终的module.exports对象并返回)
tryModuleLoad: 供require调用,用来处理读取进来的不同类型的文件(根据文件路径名的后缀名进行区分)

注意

  • 引用模块最终返回的结果,是其模块对象module的属性(对象)— exports,即module.exports;在调用tryModuleLoad方法时,将其作为exports参数的实参传了进去,所以exports拥有其引用;
    所以在模块文件中不可以对exports直接赋值(可以给它的属性赋值),因为最终返回的是module.exports。
    因为模块中的代码是被包裹起来,然后用call调用的,并在其中改变了this指向,同样指向module.exports,所以里面的this同exports可以有相同的作用。

相关文章

网友评论

      本文标题:node的require方法实现原理

      本文链接:https://www.haomeiwen.com/subject/omhkfhtx.html