Things that make you far better Node.js developer, part 2 (CommonJS)

Hossein Derakhshan
14 min readDec 26, 2022

--

In this article, I want to dive into the heart of Node.js that is responsible for managing modules. We all use modules in every Node.js application, but we might not know how it works under the hood.

In the last article, I talked about how we can add new features and syntax to javascript with the help of V8 and C++, and finally, we understood that Node.js is nothing but this. We discussed that Node.js needed to address some issues to be able to manage a server. One of these issues was managing modules.

to understand this article better, you need to know these concepts in javascript: if you already know these concepts, you can skip the prerequisite part. If not, no worries. I talk about these concepts

  • IIFE
  • prototype inheritance
  • constructed function
  • call by reference

prerequisite:

What is a module?

the simplest definition of module for me is it is a technique to reuse codes without having any code collision. That’s it.

Most programming language has their own way of managing that, but At the time Node.js was created, javascript didn’t have any feature to manage modules; however, since javascript es5, this feature is a part of JS.

What do I mean by code collision?

Let’s assume we want to reuse some codes. we can create another file, eg utils.jsand put the related codes there like:

utils.js:

var msg = "Hello, World!";
var sayHi = function() {
alert(msg);
};

Now, let’s assume we have another file, eg app.jsthat have a variable named msg . as you can see in both file we have a same variable name msg

var msg = "stinky code";

Let’s register these files in our HTML file and call sayHi() function there

<html>

<head>
<script src="js/utils.js"></script>
<script src="js/app.js"></script>
<script>
sayHi(); // it shows stinky code!!
</script>
</head>

<body>

</body>

</html>

Based on the utils.js file, we have to get Hello, World! but we see stinky code. it seems code collision happened. We can fix this problem with a technique named IIFE in JS which we will learn it soon.

What is passing by reference vs by value?

This is a really important topic, and we might see many questions in interviews regarding this topic. When we talk about passing, we either pass this variable to another function or assign it to another variable.

When a variable is declared, it is assigned a memory address in the computer like this:

var a = 10;

In javascript, we have two kinds of value:

Primitive and reference

Primitives value are:

boolean, string, number null undefined , Symbol

reference value are:

object or array

In many programming languages, if we want to pass the variable by reference or by value, they have their syntax to do this, but in javascript, it depends on the type of variable (Primitive and references) we are working with.

By value:

var a = 1;
var b = a + 1;
console.log(a,b) // 1, 2

As you can see, a and b have primitive value (number), and when we are working with Primitive types, javascript copies the value and assign it to another variable. So we have two variables that point to the different parts of memory with the same value.

By reference:

Let’s do this operation with an array

var a = [1,2];
var b = a;
b.push(3);

console.log(a)
console.log(b)

We expect that the a would be [1,2] and b would be [1,2,3] but if we run this code we see both of them have the same value which is [1,2,3]

This happens because, in non-primitive values, javascript doesn’t make a copy of the value when we assign or pass them to another variable or function. It makes a copy of the address of the variables in memory. So both of these variables are pointing to the same address in the memory

What is first class function?

First class function are part of javascript that makes this language amazing. let’s talk about them.

It means whatever you can do with string, boolean, or any other types in javascript, you can do it with function.

For example we can pass a function as an argument exactly like passing a string variable to another function:

function sayHello() {
return "Hello, ";
}
function greeting(helloMessage, name) {
console.log(helloMessage() + name);
}
greeting(sayHello, "JavaScript!");

or you write a function that returns another function eg,

function sayHello() {
return () => {
console.log("Hello!");
};
}

or you can assign a function to a variable like this:

const foo = () => {
console.log("foobar");
};
foo();

We will see how this feature can help us to make modules guys. This is something that we don’t have in many programming languages and it is so powerful.

What are IIFE (Immediately invoked function expression) and closure

IIFE is a common JavaScript pattern that executes a function immediately after it’s defined.

(function() {
// Code that runs in your function
})()

you can also assign this function to a variable like this:

var func = (function() {
return () => {
console.log('Hey')
}
})()
func() // Hey

let’s talk about the use case of IIFE:

1- Avoid code collision:

If you define any kind of variables (let, const, var) within a function in javascript, we can not access those variables outside of the function, and also, if we have two variables with the same name outside, and inside the function (like the one that we saw in previous example) , this technique can help to avoid code collision. let’s see an example:

As you can see we have a variable named msg outside and inside of the function but by the help of IIFE we avoid code collision

var msg = 'outside';
(function() {
var msg= 'inside'
console.log(msg); // inside
})()
console.log(msg) // outside

2- closure:

closure simply means when you define a function, it has access to all variables outside of the function when it was created. let’s assume we have a loop, and we want to log the index of the loop within setTimeout.

for (var i = 0; i < 6; i++) {
setTimeout(() => {
console.log(i)
}, 100)
}

if you run this code, you will see 666666 instead of 012345 .

The reason is when javascript sees an asynchronous code (in this casesetTimeout), first, it needs to execute all synchronous code. for loop here is synchronous code. so it executes for till the end and then executes all the callbacks that are provided for setTimeout. Now it’s time to execute setTimeout callbacks and at this moment, the index of the loop is 6.

That’s why it prints 666666. Now let’s fix this problem with closure:

for (var i = 0; i < 6; i++) {
var innerFunction = (index) => {
setTimeout(() => {
console.log(index)
}, 100)
}
innerFunction(i)
}

if you run this code, you will see 012345 because when innerFunction is created, it captures all the variables at that moment. BTW we can write the above code with IIFE format like this

for (var i = 0; i < 6; i++) {
((index) => {
setTimeout(() => {
console.log(index)
}, 100)
})(i)
}

Another use case of closure is we can define private and public variables inside the method. can you guess how? put a comment if you know :)

What is object literal?

If you’ve used javascript, you probably have used object literal and what object is, but maybe you don’t know its name. It is like a JSON. the only difference here is in JSON keys are covered in the double quote.

Object literal in javascript is a way of creating a group of different data. we can define as a name-value pairs inside of curly braces like this:

var greeting = {
fullname: "Michael Jackson",
greet: (message, name) => {
console.log(message + " " + name + "!!");
},
gender: "Male",
getFullData: () => {
return this.fullname + this.gender;
}
};

now we can assign or access to any properties in the greeting object with . operator:

greeting.greet();
greeting.fullname = 'Hey this is test';

what is constructed function?

It is a normal function that creates an object. in javascript there are many other ways to create an object. constructed function is one of them.

function Person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
}
var obj = new Person('Hossein', 'Derakhshan', 33, 'Black');

as you can see with new keyword, we can define a new object in js through constructed function. now if we logobj we will see:

there is another property named [[Prototype]] let’s see what it is

What is prototype in Javascript?

By the help of this feature, we can kind of implements inheritance in javascript. I’m saying kind of because JavaScript is not a class-based object-oriented language.

when we create a constructor function, javascript will add a property named prototype to that function. Then if you assign any property or methods to the prototype there will be available in instantiate object.

let’s see this example:

function Person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
this.getFullName2 = function() {
return this.firstName + this.lastName;
}
}

Now let’s add a method name getFullName to the prototype of this function:

Person.prototype.getFullName = function() {
return this.firstName + this.lastName;
}

now let’s initiate Person by new keyword

var hossein = new Person('hossein' ,'derakhshan', 32, 'black');
console.log(hossein.getFullName()); // hossein derakhshan

as you can see when you create a new object from Person, that object has access to the getFullName method even though this method is not defined in Person function itself.

You might say that hey why not adding getFullName directly to the Person class itself just like getFullName2 . to get this answer let’s initiate another object from Person

var ali = new Person('ali' ,'rezai', 32, 'black');

now let’s see compare getFullName2 which is defined inside a Person function with getFullName which is assigned to the prototype

console.log(hossein.getFullName2 === ali.getFullName2); // false
console.log(hossein.getFullName === ali.getFullName); // true

as you can see in this code when we compare getFullName2, in both initiated objects (Ali and Hossein) the result is false meaning it takes another space of the memory ,but when we compare getFullName the result is true ,which means both objects are pointed to the same memory address. So we are saving memory. we must consider when JS was invented, computers had very low memory.

PS: es6 classes, it is just a syntactic sugar or a convenient syntax over es5 syntax. under the hood, even if you use es6 classes, it is still using prototype and es5 syntax.

what is Reflect.apply?

Reflect.apply is a build-in javascript method which is used to call a function using the specified argument. It works similar to the Function.prototype.apply() method to call a function, but in an efficient manner and easy to understand.

Reflect.apply(targetFunction, thisArgument, argumentsList)

Here is an example:

function func (a, b, c) { 
this.x = a;
this.y = b;
this.z = c;
}
const obj = {};
Reflect.apply ( func , obj, [12,42,32] ); 
console.log( obj );
{ x: 12, y: 42, z: 32 }

Now that we know the basic concepts here, let’s back to the topic :)

How Node.js fixed code collision?

CommonJS is the way that Node.js address code collision. when we run node file.js we are passing the javascript file to the node.js. we know that Node.js at the end, will pass this javascript file to the v8 and then will give us the result.

let’s assume that this is our node.js application:

utils.js

var msg = "Hello, World!";
var sayHi = function() {
console.log(msg);
};
module.exports = sayHi; // node.js has added this syntaxt to JS

app.js

var whatever = require('./utils.js') // node.js has added this syntaxt to JS
var msg = 'something'
whatever();

we can run it with node app.js . we know that If node.js forward this javascript to v8 directly, we will get some errors. let’s do it in chrome console to see the error:

we are getting this error because parser in v8 , can not understand this syntax. so how do we not get any error in node.js?

in order to understand better, let’s just run node without providing any file name in a directory that these files(app.js, utils.js) exist.

If you run node in the console, you should see something like this depending on the version of your node.js

Now, if you type this command in the CLI require('./app'), you will see the same result as calling node ./app which means behind the scene node.js call the require function on the entry point.

The good news is require is a javascript function (it is not a pure C++ function), that’s why we can debug it :) let’s dive into the internal part of node.js and see what will happen after calling the require function.

On the right side, in scope section, we see some local variables like (exports, module, msg, require, __dirname, …) which is not exist in the app.js let’s step into the require function (you will get your answer).

depends on the version of your node.js, at the end you should see something like above picture.

let’s step into more:

as you can see in line 1020 require attached to the Module through prototype (line 1020). we already know what prototype do.

This line once again proves that require function has been called on app.js when we ran node app.js , and it created a Module class, and this class has access to the require function.

if we step into the Module.load we will see a lot of codes, but I tried to delete the lines that are not related to this topic. so the simpler version of module.load should be something like this:

Module._load = function(request, parent, isMain) {
const filename = Module._resolveFilename(request, parent, isMain);

const module = new Module(filename, parent);
module.load(filename);

return module.exports;
};

let’s break down this function:

const filename = Module._resolveFilename(request, parent, isMain);

this line is going to get the full file path on the local drive. in my case:

/Users/hossein/Tools/Learning/testt/utils.js

then let’s move to the next line:

const module = new Module(filename, parent);

this is the constructed function that we are already familiar with its concept. So it should be a function with a name Module right? let’s see that

function Module(id = '', parent) {
this.id = id;
this.path = path.dirname(id);
setOwnProperty(this, 'exports', {});
moduleParentCache.set(this, parent);
updateChildren(parent, this, false);
this.filename = null;
this.loaded = false;
this.children = [];
}:

if we run this function, it creates an object like this:

as you can see, it creates an empty exports: {} and also it has access to a lot of methods (like require and …) through prototype

Now let’s check this section:

module.load(filename);

First of all, this line doesn’t return anything, meaning that it should add something to the module object itself. in other words, load should do some call-by-reference operation.

if we step into the load function we will see this:

On the right side of this module, there is a variable named extention and the value is .js

this gives us the idea that this class can load other types of data like json or . and if we don’t provide the .js in the require function, the default value would be .js . let’s check this line

 Module._extensions[extension](this, filename);

Based on this line,Module._extensions should be an object. here is its value:

what we are doing in that line is we are calling a function based on the extension. let’s dive more:

Simplified version of this function can be like this:

Module._extensions['.js'] = function(module, filename) {
var content = fs.readFileSync(filename, 'utf8');
module._compile(content, filename);
};

This function is going to read the file from disk based on the filename. This is something that javascript can not do. but in Node.js there is a class named fs to work with files. fs.readFileSync is a combination of c++ and javascript, and now we don’t want to dive into this function. but all it does is it reads the file.

so content variable should be the content of the utils.js file:

‘var msg = “Hello, World!”;\nvar sayHi = function() {\n console.log(msg);\n};\nmodule.exports = sayHi;’

and finally, let’s step into the last and most interesting function guys. module._compile

this function is going to pass the compiled javascript into the v8 and execute it. I tried to simplify this function so the code should be something like this:

Module.prototype._compile = function(content, filename) {
const compiledWrapper = wrapSafe(filename, content, this);
const dirname = path.dirname(filename);
const require = makeRequireFunction(this, redirects);
let result;
const exports = this.exports;
const thisValue = exports;
const module = this;
result = ReflectApply(compiledWrapper, thisValue,
[exports, require, module, filename, dirname]);
return result;
};

let’s break down this function and check the first line:

const compiledWrapper = wrapSafe(filename, content, this);

based on the name (wrapSafe) , this function should add something to the currentcontent which was:

‘var msg = “Hello, World!”;\nvar sayHi = function() {\n console.log(msg);\n};\nmodule.exports = sayHi;’

I tried to simplify the wrapSafe function for you.

All it does is just add two strings into our script

let wrap = function(script) {
const wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});',
];
return wrapper[0] + script + wrapper[1];
};

if we run this function, we will get this result:

(exports, require, module, __filename, __dirname) {
var msg = "Hello, World!";
var sayHi = function() {
console.log(msg);
};
module.exports = sayHi;
})

See? it just wraps our code with a function to avoid the code collision. but this function is not executed yet. in fact, ReflectApply is going to execute our codes by passing this function to v8 and get back the result.

result = ReflectApply(compiledWrapper, thisValue,
[exports, require, module, filename, dirname]);

let’s jump back to the image that you already saw at the beginning of this article:

All the variables (exports, module, msg, require, __dirname) we saw in local scope come from the wrapFunction:

(exports, require, module, __filename, __dirname) {})

let’s see the module argument. as you might remember module was an object like this with exports = {}

we are passing this object to this function and then we run the function with ReflectApply .

We are passing this object in the inner function because if we assign something to the module.exports in the inner function, we want access to it from outside of the wrapped function.

let’s simplify this:

var obj = {
exports: {}
}

((module) => {

var msg = "Hello, World!";
var sayHi = function() {
console.log(msg);
};

module.exports = sayHi;
})(obj)

if (obj.exports) {
obj.exports()
}

I just created an object named obj then I created an IIFE and I passed the obj object into IIFE and I put a name as a module. In the inner function, I assigned sayHi function in module.exports . since I’m changing the reference of exports, outside of the IIFE I still have access to the module.exports !

If we check the last line of require function, we will see that it returns the module.exports

Module._load = function(request, parent, isMain) {
const filename = Module._resolveFilename(request, parent, isMain);

const module = new Module(filename, parent);
module.load(filename);

return module.exports; // <<<<<<<<<< this line
};

That’s pretty much it! Love you and stay tuned :)

--

--

Hossein Derakhshan

Hey, I'm Hossein. I'm a javascript developer who wants to know what is going on behind the scenes of these tools :)