In this Tutorial we will create a simple code profiler, an object with the following properties:

  • A start() method that triggers the start of a profiling session
  • An end() method to terminate the session and log its execution time to the console

Let’s start by creating a file named profiler.js, which will have the following content:

class Profiler { 
  constructor(label) { 
    this.label = label; 
    this.lastTime = null; 
  start() { 
    this.lastTime = process.hrtime(); 
  end() { 
    const diff = process.hrtime(this.lastTime); 
      `Timer "${this.label}" took ${diff[0]} seconds and ${diff[1]} 

There is nothing fancy in the preceding class; we simply use the default high resolution timer to save the current time when start() is invoked, and then calculate the elapsed time when end() is executed, printing the result to the console.

Now, if we are going to use such a profiler in a real-world application to calculate the execution time of the different routines, we can easily imagine the huge amount of logging we will generate to the standard output, especially in a production environment. What we might want to do instead is redirect the profiling information to another source, for example, a database, or alternatively, disable the profiler altogether if the application is running in production mode. It’s clear that if we were to instantiate a Profiler object directly by using the new operator, we would need some extra logic in the client code or in the Profiler object itself in order to switch between the different logics. We can instead use a factory to abstract the creation of the Profiler object, so that, depending on whether the application runs in production or development mode, we can return a fully working Profiler object, or alternatively, a mock object with the same interface, but with empty methods. Let’s do this then in the profiler.js module, instead of exporting the Profiler constructor, we will export only a function, our factory. The following is its code:

module.exports = function(label) { 
  if(process.env.NODE_ENV === 'development') { 
    return new Profiler(label);                       //[1] 
  } else if(process.env.NODE_ENV === 'production') { 
    return {                                          //[2] 
      start: function() {}, 
      end: function() {} 
  } else { 
    throw new Error('Must set NODE_ENV'); 

The factory that we created abstracts the creation of a Profiler object from its implementation:

  • If the application is running in development mode, we return a new, fully functional Profiler object
  • If instead the application is running in production mode, we return a mock object where the start() and stop() methods are empty functions

The nice feature to highlight is that, thanks to JavaScript dynamic typing, we were able to return an object instantiated with the new operator in one circumstance and a simple object literal in the other (this is also known as duck typing Our factory is doing its job perfectly; we really can create objects in any way that we like inside the factory function, and we can execute additional initialization steps or return a different type of object based on particular conditions, and all of this while isolating the consumer of the object from all these details. We can easily understand the power of this simple pattern.

Now we can play with our profiler; this is a possible use case for the factory that we just created earlier:

const profiler = require('./profiler'); 
function getRandomArray(len) { 
  const p = profiler('Generating a ' + len + ' items long array'); 
  const arr = []; 
  for(let i = 0; i < len; i++) { 

The p variable contains the instance of our Profiler object, but we don’t know how it’s created and what its implementation is at this point in the code.

If we include the preceding code in a file named profilerTest.js, we can easily test these assumptions. To try the program with profiling enabled, run the following command:

export NODE_ENV=development; node profilerTest

The preceding command enables the real profiler and prints the profiling information to the console. If we want to try the mock profiler instead, we can run the following command:

export NODE_ENV=production; node profilerTest

The example that we just presented is just a simple application of the factory function pattern, but it clearly shows the advantages of separating an object’s creation from its implementation.