Snipper from source code
Like the answer below says:
https://stackoverflow.com/a/22771432
When JavaScript files are require()
d as Node modules, the Node engine runs the module code inside of a wrapper function. That module-wrapping function is invoked with a this
set to module.exports
This is what I don't understand, why calling it module.exports
rather than just module
?
The wrapped function is passed module.exports
as this
keyword but why don't we just pass module
You can look at the loader code yourself here. Where the this
value for a module is set is actually here.
I can't say why they decided it should be the exports
object rather than module
object. Perhaps because that's what they expect you to assign to and it's certainly more common to reference exports
than it is module
.
Both the module
object and the exports
objects are passed as an argument to the wrapper and are in scope for your code so you can reference either one without using this
and, in my opinion, it makes for clearer code to use module
or module.exports
directly rather than using this
.
Here's a copy of the relevant code from the above link. The this
value is set in the ReflectApply()
call near the bottom of this block in the variable named thisValue
.
// Run the file contents in the correct scope or sandbox. Expose
// the correct helper variables (require, module, exports) to
// the file.
// Returns exception, if any.
Module.prototype._compile = function(content, filename) {
let moduleURL;
let redirects;
if (policy?.manifest) {
moduleURL = pathToFileURL(filename);
redirects = policy.manifest.getDependencyMapper(moduleURL);
policy.manifest.assertIntegrity(moduleURL, content);
}
maybeCacheSourceMap(filename, content, this);
const compiledWrapper = wrapSafe(filename, content, this);
let inspectorWrapper = null;
if (getOptionValue('--inspect-brk') && process._eval == null) {
if (!resolvedArgv) {
// We enter the repl if we're not given a filename argument.
if (process.argv[1]) {
try {
resolvedArgv = Module._resolveFilename(process.argv[1], null, false);
} catch {
// We only expect this codepath to be reached in the case of a
// preloaded module (it will fail earlier with the main entry)
assert(ArrayIsArray(getOptionValue('--require')));
}
} else {
resolvedArgv = 'repl';
}
}
// Set breakpoint on module start
if (resolvedArgv && !hasPausedEntry && filename === resolvedArgv) {
hasPausedEntry = true;
inspectorWrapper = internalBinding('inspector').callAndPauseOnStart;
}
}
const dirname = path.dirname(filename);
const require = makeRequireFunction(this, redirects);
let result;
const exports = this.exports;
const thisValue = exports;
const module = this;
if (requireDepth === 0) statCache = new SafeMap();
if (inspectorWrapper) {
result = inspectorWrapper(compiledWrapper, thisValue, exports,
require, module, filename, dirname);
} else {
result = ReflectApply(compiledWrapper, thisValue,
[exports, require, module, filename, dirname]);
}
hasLoadedAnyUserCJSModule = true;
if (requireDepth === 0) statCache = null;
return result;
};