• 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}`



  • 相关阅读:
    animation——鼠标放上图片旋转
    docker安装Redis并设置密码
    Docker安装MySQL详细教程(mysql是5.7版本,可以根据自己需要修改版本)
    Linux安装jdk(两种方式)
    Nginx http 反向代理高级应用
    jenkins 远程启动tomcat报错:Neither the JAVA_HOME nor the JRE_HOME environment variable is defined
    从一个Git仓库转移到另外一个仓库
    使用Spring @DependsOn控制bean加载顺序
    Swagger2异常:Illegal DefaultValue null for parameter type integer java
    springboot的实体类Integer和int如何选择
  • 原文地址:https://www.cnblogs.com/lyraLee/p/12169469.html
Copyright © 2020-2023  润新知