• nodeJS中require方法的自实现


    nodeJS模块化,使用commonJS规范,该规范以读取文件实现模块化。

    (function(exports,require, module, __filename,__dirname) {
      module.export = XXX;
      return module.exports;
    })

    1. commonJS规范:

    1. 文件即模块。(读取的文件是字符串)
    2. 定义了导出文件的方式module.exports 和 exports
    3. 定义了引入文件的方式require
     
    **浏览器中让字符串运行**
    eval / new Function
    **nodeJS中字符串运行**
    const vm = require('vm'); vm.runInThisContext(str);

    2. require方法的实现

    * 内部实现了一个内置的require方法
    * 使用`Module._load`方法加载模块
    * 使用`Module.__resolveFilename`,将相对路径=>绝对路径+文件后缀
    * 缓存机制`Module._cache`,缓存模块
    * 新建一个模块 `new Module`;Module有两个主要属性id(路径), exports={}
    * 使用`tryModuleLoad`尝试加载模块
        * 获取文件后缀
        * 通过`Module._extensions`上后缀对应的方法加载模块->读取文件
        * `Module.wrap`包裹读取的字符串内容;
        * `runInThisContext`运行包裹后的字符串;将字符串转为函数
        * 运行函数,并将this.exports(默认空对象)作为this绑定到函数的this上
    let path = require('path');
    let fs = require('fs');
    let vm = require('vm');
    
    function Module(path){
      this.id = path;
      this.exports = {};
    }
    Module.wrapper = [
      '(function(exports, require, module, __filename,__dirname){',
      '})'
    ]
    Module.extensions = {
      '.js': function(module) {
        let content = fs.readFileSync(module.id, 'utf8');
        let fnStr = Module.wrapper[0] + content + Module.wrapper[1];
        let wrapperFn = vm.runInThisContext(fnStr);
        wrapperFn.call(module.exports, module.exports, req, module, __filename, __dirname);
        // 该方法是用户自定义给module.exports赋值;
      }, 
      '.json': function(module) {
        let json = fs.readFileSync(module.id, 'utf8');
        module.exports = json;
      },
      '.node': {
        //
      }
    }
    function tryModuleLoad(module) {
      let extension = path.extname(module.id);
    
      Module.extensions[extension](module);
    }
    Module._cache = {};
    function req(modulePath) {
      let resolvedPath = path.resolve(modulePath);
      // 如果路径的文件未写扩展名;需要按照默认扩展名依次查找
      let i = 0;
      function findFilePath(parsePath) {
        try {
          fs.accessSync(parsePath);
          return parsePath;
        } catch(e){
          let extensions = Object.keys(Module.extensions);
          let tempPath= resolvedPath + extensions[i++];
          return findFilePath(tempPath);
        }
      }
      let absolutePath = findFilePath(resolvedPath);
      if(Module._cache[absolutePath]) {
        return Module._cache[absolutePath].exports;
      }
      const module = new Module(absolutePath);
      tryModuleLoad(module);
      Module._cache[absolutePath] = module;
      return module.exports;
    }
    
    console.log(req('./1'));

    3. module.exports和exports的区别

    1. exports是module.exports的别名,两者指向地址相同;
    2. 但是模块导出的是module.exports。如果使用`exports = xxxx`导出,则exports地址修改。
    而module.exports初始值是{},则模块最终导出的是{}
    3. 如果一定要用exports,则可以通过给exports对象添加属性,则相当于同时给module.exports添加。
    如:`exports.a = xxx;`
    最终的导出结果是`{a: xxx}`



  • 相关阅读:
    Azure SQL Storage
    T-SQL quries
    映射盘符
    繁体及其输入法、乱码问题
    匈牙利命名法
    C++四种转换总结
    windows系统下进程间通信
    Qt 中文字符串问题
    PDB文件详解
    DbgView 无法开启Capture Kernel问题
  • 原文地址:https://www.cnblogs.com/lyraLee/p/12169469.html
Copyright © 2020-2023  润新知