cicciosgamino
cicciosgamino

Reputation: 964

Javascript Module in Javascript

Ok the problem is simple. I want use two JavaScript files: one following the module pattern and another one that calls the firs one. I tested all the code using Node.js. When all the code is in one file it works but if I split the code in two files I get an error.

Here's the code:

 // module.js

var testModule = (function(){
"use strict";

var counter = 0;

return {
    incrementCounter : function(){
        return counter++;
    },
    resetCounter : function(){
        console.log("Last Counter Value before RESET :" + counter);
        counter = 0;
    };
};

 })();

 // script.js 

 var testModule = require('./module.js');  // i'm not sure about require or import
 "use strict";

testModule.incrementCounter();
testModule.resetCounter();

PS: I want use the Javascript notation not the Node export notation to implement the pattern.

Upvotes: 1

Views: 451

Answers (1)

Edwin Dalorzo
Edwin Dalorzo

Reputation: 78579

I will start by saying that it is not clear why you want to use the "module pattern" with Node.js. I mean, the module pattern that you suggest make more sense in the client-side JavaScript, that is, JavaScript in the browser, but if your code is intended to run in Node.js then you can exploit the module functionality already present in Node and I personally do not see value in forcing the use of the module pattern in this case.

So, I will delve first in different ways to use the Node.js module pattern, and at the end I explain how I would combine the "module pattern" and the "Node.js module pattern".

On Modules, Import and Export

Let’s start by the most obvious and simple thing. Something probably everyone learns since the first day of work with Node: every code file is considered a module. The variables, properties, functions, constructors that we declared in it are private to the module and other modules cannot gain access to them or use them unless the programmer of the module explicitly expose them to the public; namely everything we declare inside a module is encapsulated and hidden from the outside world by default unless explicitly stated otherwise. To expose something the programmer has access to a special object called module, which has a special property called exports. Everything that you publish in the module.exports object is made publicly available to other modules. For instance, in the code below, the variable pi is inaccessible to any other modules but foo.js, whereas the property named bar is made publicly available to any other modules importing the module foo.js. Note that this is a fundamental difference from JavaScript in Node.js when compared with JavaScript as executed in a browser where functions in a JavaScript file may be publicly exposed in a global object (i.e. window).

//module foo.js
var pi = 3.14;
module.exports.bar = 'Hello World';

Now a second module baz.js can “import” the module foo.js and gain access to the property bar. In Node, we achieve this effect by means of using a global function named require. Somewhat as follows:

//module baz.js
var foo = require('./foo');
console.log(foo.bar); //yields Hello World

Technique 1 – Extending exports Object with Additional Functionality

So, one technique to expose the functionality in a module consists in adding functions and properties to the module.exports object. When this is the case, Node provides a direct access to the exports object to make things simpler for us. For instance:

//module foo.js
exports.serviceOne = function(){ };
exports.serviceTwo = function(){ };
exports.serviceThree = function(){ };

And as you might expect, the users of this module, at importing it, would obtain a reference to the exports object and by this they would gain access to all the functionality exposed in it.

//module bar.js
var foo = require('./foo');
foo.serviceOne();
foo.serviceTwo();
foo.serviceThree();

Technique 2 – Substitute Default exports Object with Another Object

By this point you probably suspect that given the fact that module.exports is just an object that exposes the public part of a module then we could probably define our own object and then replace the default module.exports object with our own. For instance:

//module foo.js
var service = {
   serviceOne: function(){ },
   serviceTwo: function(){ },
   serviceThree = function(){ }
};

module.exports = service;

The code in this last example would behave exactly as the code in the previous example, it’s just that this time we have explicitly created our exported object instead of using the one provided by default by Node.

Technique 3 – Substitute Default exports Object with a Constructor Function

In the examples so far we have always used an instance of an object as our exposed target. However there are occasions in which it may seem more convenient to allow the user to create as many instances of a given type as she wants. Nothing prevents us from replacing the module.exports object with other types of objects like a constructor function. In the example below we expose a constructor which the user can use to create many instances of the Foo type.

//module Foo.js
function Foo(name){
   this.name = name;
}

Foo.prototype.serviceOne = function(){ };
Foo.prototype.serviceTwo = function(){ };
Foo.prototype.serviceThree = function(){ };

module.exports = Foo;

And the user of this module can simply do something like this:

//module bar.js
var Foo = require('./Foo');
var foo = new Foo('Obi-wan');
foo.serviceOne();
foo.serviceTwo();
foo.serviceThree();

Technique 4 – Substitute Default exports Object with Plain Old Function

It is easy to imagine now that if we can use a constructor function then we might just as well be able to use any other plain old JavaScript function as the target exposed in module.exports. As in the following example in which our exported function allows the user of this module to gain access to one of several other encapsulated service objects.

//foo.js
var serviceA = {};
serviceA.serviceOne = function(){ };
serviceA.serviceTwo = function(){ };
serviceA.serviceThree = function(){ };

var serviceB = {};
serviceB.serviceOne = function(){ };
serviceB.serviceTwo = function(){ };
serviceB.serviceThree = function(){ };

module.exports = function(name){
   switch(name){
      case 'A': return serviceA;
      case 'B': return serviceB;
      default: throw new Error('Unknown service name: ' + name);
   }
};

Now the user that imports this module receives a reference to our anonymous function declared above and then she can simply invoke the function to gain access to one of our encapsulated objects. For instance:

//module bar.js
var foo = require('./foo');
var obj = foo('A');
obj.serviceOne();
obj.serviceTwo();
obj.serviceThree();

Many programmers ordinarily invoke the function immediately returned by require instead of assigning it to a reference first. For instance:

//module bar.js
var foo = require('./foo')('A');
foo.serviceOne();
foo.serviceTwo();
foo.serviceThree();

So, in summary, it is as simple as follows: everything that we expose in module.exports is what we get when we invoke require. And using different techniques we could expose objects, constructors functions, properties, etc.

Based on all these examples I say that it does not make sense for me to use a the module pattern in your code.

Using Module Pattern and Node.js Modules

However, if you would be creating a library that you would like to use in both Node.js and in the browser, then using both patterns could make sense. That is not evident in your question, though. But if that's the case, then you could combine the two ideas together.

For instance, doing something like this:

var TestModule;

(function (TestModule) {

    var counter = 0;
    TestModule.incrementCounter = function(){
        return counter++;
    };
    TestModule.resetCounter = function(){
        console.log('Last Counter Value before RESET :' + counter);
        counter = 0;
    };

    return TestModule;

})(typeof module === 'undefined' 
     ? (TestModule || (TestModule = {})) 
     : module.exports);

When running in Node.js, then within the IIFE the TestModule corresponds to the module.exports object, when running in the browser, the TestModule represents a namespace.

So, if you are running in Node, you can do:

var testModule = require('./testModule');
testModule.incrementCounter();

And if you are running in the browser, then after loading this script you can access the namespace TestModule directly.

TestModule.incrementCounter();

Upvotes: 2

Related Questions