Exploring Node.js Modules: Hiding implementation details with encapsulation in CommonJS and ES6 modules

We’ve already seen a couple of examples of how modules hide implementation details with the simple.js example and the programs we examined in Chapter 2, Setting up Node.js. Let’s take a closer look.

Node.js modules provide a simple encapsulation mechanism to hide implementation details while exposing an API. To review, in CommonJS modules the exposed API is assigned to the module.exports object, while in ES6 modules the exposed API is declared with the export keyword. Everything else inside a module is not available to code outside the module.

In practice, CommonJS modules are treated as if they were written as follows:

(function(exports, require, module, filename, dirname) {

// Module code actually lives in here

});

Thus, everything within the module is contained within an anonymous private namespace context. This is how the global object problem is resolved: everything in a module that looks global is actually contained within a private context. This also explains how the injected variables are actually injected into the module. They are parameters to the function that creates the module.

The other advantage is code safety. Because the private code in a module is stashed in a private namespace, it is impossible for code outside the module to access the private code or data.

Let’s take a look at a practical demonstration of the encapsulation. Create a file named module1.js, containing the following:

const A = “value A”;

const B = “value B”;

exports.values = function() {

return { A: A, B: B };

}

Then, create a file named module2.js, containing the following:

const util = require(‘util’);

const A = “a different value A”;

const B = “a different value B”;

const m1 = require(‘./module1’);

console.log(‘A=${A} B=${B} values=${util.inspect(m1.values())}’);

console.log(‘${m1.A} ${m1.B}’);

const vals = m1.values();

vals.B = “something completely different”;

console.log(util.inspect(vals));

console.log(util.inspect(m1.values()));

Using these two modules we can see how each module is its own protected bubble. Then run it as follows:

$ node module2.js

A=a different value A B=a different value B values={ A: ‘value A’, B: ‘value B’ }

undefined undefined

{ A: ‘value A’, B: ‘something completely different’ }

{ A: ‘value A’, B: ‘value B’ }

 

This artificial example demonstrates encapsulation of the values in module1.js from those in module2.js. The A and B values in module1.js don’t overwrite A and B in module2.js because they’re encapsulated within module1.js. The values function in module1.js does allow code in module2.js access to the values; however, module2.js cannot directly access those values. We can modify the object module2.js received from module1.js. But doing so does not change the values within module1.js.

In Node.js modules can also be data, not just code.

Source: Herron David (2020), Node.js Web Development: Server-side web development made easy with Node 14 using practical examples, Packt Publishing.

Leave a Reply

Your email address will not be published. Required fields are marked *