node源码

const path = require("path");
const fs = require("fs");
const vm = require("vm");

function Module(id) {
  this.id = id;
  this.exports = {};
}

Module.wrap = function (script) {
  let arr = [
    "(function (exports, require, module, __filename, __dirname){",
    script,
    "})",
  ];
  return arr.join("");
};

Module._extensions = {
  ".js": function (module) {
    let content = fs.readFileSync(module.id, "utf8");
    let fnStr = Module.wrap(content);
    let fn = vm.runInThisContext(fnStr);
    let exports = module.exports;
    let require = myRequire;
    let __filename = module.id;
    let __dirname = path.dirname(module.id);
    // 这里的this   就是exports对象
    fn.call(exports,exports, require, module, __filename, __dirname );
    // 用户会给exports赋值
    // console.log(fn.toString());
  },
  ".json": function (module) {
    let content = fs.readFileSync(module.id);
    module.exports = JSON.parse(content);
  },
};

Module._resolveFilename = function (filepath) {
  let filePath = path.resolve(__dirname, filepath);
  let exists = fs.existsSync(filePath);
  if (exists) return filePath;

  // 尝试添加后缀
  let keys = Object.keys(Module._extensions);
  for (let i = 0; i < keys.length; i++) {
    let curentPath = filePath + keys[i];
    if (fs.existsSync(curentPath)) {
      return curentPath;
    }
  }
};

Module.prototype.load = function (filename) {
  // 获取文件的后缀进行加载
  let extname = path.extname(filename);
  Module._extensions[extname](this); // 根据对应的后缀名进行加载
};

Module._load = function (filepath) {
  // 将路径转化成绝对路径
  let filename = Module._resolveFilename(filepath);
  let module = new Module(filename);
  module.load(filename);

  return module.exports;
};

function myRequire(filepath) {
  //根据路径加载这个模块
  return Module._load(filepath);
}

exports/module.exports/this 的关系

fn.call(exports,exports, require, module, __filename, __dirname );
从代码中我们可以看到this就是exports。
a.js文件代码

console.log(this === exports); // true
console.log(this === module.exports); // true
console.log(module.exports === exports); // true

module.exports = 1;

use.js文件

let r = require("./a");  // 同步语法
console.log(r);

从上面的代码我们可以确认
this === exports === module.exports

exports和module.exports 输出结果为什么不一样

既然this === exports === module.exports,为什么在a.js文件中下面的代码获取结果不一样呢?

exports = 1; // 如果使用exports = 1, 获取的时候得到的是值是{}
module.exports = 1; // 获取的值是1

看下面代码,思考为什么这样

  let a = b = {}
  a = 1;
  console.log(b); // 输出结果是{},而不是1
  let a = b = {}
  a = 'hello';
  console.log(b);// 输出结果是{},而不是1
  let a = b = {}
  a = {name:'fung', age:3};
  console.log(b); // 输出结果是{},而不是1
let a = b = {}
a.name = 'a';
a.age = 3;
console.log(b); // 输出结果是{ name: 'a', age: 3 }, 而不是{}

上面三个代码例子就可以说明为什么exports和module.exports 输出结果不一样。
开始的时候

let exports = module.exports = {}
fn.call(exports,exports, require, module, __filename, __dirname );
return module.exports

开始exports module.exports this都指向同一个引用地址,当我们给exports赋值的时候,则断开了exports的指向,并不会改变module.exports和this的指向问题。
但是下面代码的输出结果则是一样的

 module.exports.a = 1 // 输出{ a: 1 }
 exports.a = 1 // 输出{ a: 1 }
 this.a = 1  // 输出{ a: 1 }

大煜儿
106 声望7 粉丝

用心走路,给每一个细节打一个结。