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); // trueconsole.log(this === module.exports); // trueconsole.log(module.exports === exports); // truemodule.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 }