Understanding Node Module.exports vs exports and what "this" refers to

This is extrapolated from the following stackoverflow post:

To understand the difference between Module.exports and exports, you have to understand how require() is implemented.

First a Module object is created:

function Module(id, parent) {
  this.id = id;
  this.exports = {};
  this.parent = parent;
  if (parent && parent.children) {
    parent.children.push(this);
  }

  this.filename = null;
  this.loaded = false;
  this.children = [];
}

Then the code from the required file is wrapped in an IIFE to give it function scope

(function (exports, require, module, __filename, __dirname) {
    //contents from file go here
}) (module.exports,require, module, "path_to_file1.js","directory of the file1.js");

As you can see in the IIFE parameter list, exports is a reference to the object that module.exports refers to. Code in the file executes inside the anonymous function. Because JavaScript passes by value, inside the IIFE, exports is a COPY of the reference to module.exports. Assigning something to exports inside the IIFE will overwrite the reference. For example, if you write something like this:

exports = {save: function save(){...}}

You will have assigned a new object reference to the exports parameter so exports is no longer a reference to module.exports.

require() returns module.exports, not exports, so if you were to require the file now, you would not see the save function because it was assigned to the local exports variable instead of module.exports.

Typically, people only assign properties to exports, like so:

exports.save = function save(){...}

Here you are not changing what exports references, you are simply adding a property to module.exports because exports is a reference to module.exports until you overwrite it.

Because the code your module exports is attached as properties to the module.exports object, unless you are inside one of your functions or some other context YOU have defined, “this” will point to the module.exports object. Therefore, you can refer to other exported properties and functions on module.exports by using this. Variables not exported are local variables inside the IIFE, and can simply be referred to by name. Keep this in mind if you are, for example, cloning a module as a form of concatenative inheritance. When you require(‘path/to/file’), the file is put into its own IIFE. If code in the file refers to a local IIFE variable, the code will still refer to it even after it’s cloned. For example:

var mod1 = require('path/to/file1');
var mod2 = _.clone(mod1);

If elsewhere, you do this:

mode2 = require('path/to/file2');
mod2.someFunc();

If someFunc() is code cloned from mod1, and it references a local IIFE variable, it will refer to the local variable in mod1 if it is defined there instead of the local variable in mod2 because the IIFE in mod1 is an inner function.