read

With the most recent versions of JavaScript, we now have more options for creating closures to mimic a private scope that you find in languages like C#. The IIFE pattern has been in heavy use for years, but the concern is that it can cause memory leaks that have the potential to bring your application to its knees.

Here's one possible construct to use that I developed with input from the instruction team at Nashville Software School. It uses simple block scope to isolate a module's code from global scope.

I will create two modules - one for defining a doctor, and one for a patient in a simple doctor's office application.

I use WeakMap to weakly store object references, which will allow the "private" information to be released if the enclosing object needs to be garbage collected. You can read more about that in my WeakMap for JavaScript Private Data article.

Storage and Access Objects

First step is to create the main object.

// Create the object upon which all other objects will be created
let DoctorOffice = Object.create({})  

Next, create a block scope and create my WeakMap to hold object references. This prevents external code from gaining access to the instanceStorage variable.

// Create the object upon which all other objects will be created
let DoctorOffice = Object.create({})

{
  // WeakMap to store private properties of the patient object
  const InstanceStorage = new WeakMap();    
}

Now I can define a function that will be used to store & get object references.

// Create the object upon which all other objects will be created
let DoctorOffice = Object.create({})

{
  // WeakMap to store private properties of the patient object
  const InstanceStorage = new WeakMap();    

  /*
    Function to return the object instance from the WeakMap.
    I only put it in all caps to make it stand out in this
    article's code.
  */
  const PRIVATE = function (object) {
    if (!InstanceStorage.has(object))
        InstanceStorage.set(object, Object.create(null));
    return InstanceStorage.get(object);
  }
}

Doctor Object

Now that I have a store for weakly held object references, and a function to manage it, it's time to define the doctor object for use in the doctor's office.

{
  // ...

  // Create DoctorOffice.Doctor namespace
  DoctorOffice.Doctor = Object.create({}, {
    // Initializer function
    init: {
      value: function () {
        PRIVATE(this).name = ""
        PRIVATE(this).speciality = ""
        PRIVATE(this).patients = []
      }
    }
  })
}

Since the name, specialty, and patients properties aren't on the actual object itself, but rather the object that got created in InstanceStorage, I need to provide some get/set properties for them, and then a method for adding a patient to the patients property.

{
  ...

  DoctorOffice.Doctor = Object.create({}, {
    // Initializer function
    init: {
      value: function () {
        PRIVATE(this).name = "doctor"
        PRIVATE(this).specialty = "specialty"
        PRIVATE(this).patients = []
      }
    },
    /*
      Public scope property that get/set a private property
      on the object stored in the WeakMap
    */
    name: {
      enumerable: true,
      get: function () {
        return PRIVATE(this).name
      },
      set: function (n) {
        PRIVATE(this).name = n
      }
    },
    /*
      Public scope property that get/set a private property
      on the object stored in the WeakMap
    */
    specialty: {
      enumerable: true,
      get: function () {
        return PRIVATE(this).specialty
      },
      set: function (n) {
        PRIVATE(this).specialty = n
      }
    },
    /*
      Public scope, read-only property 
    */
    patients: {
      enumerable: true,
      get: function () {
        return PRIVATE(this).patients
      }
    },
    /*
      Public scope method
    */
    addPatient: {
      value: function (patient) {
        PRIVATE(this).patients.push(patient)
      }
    }
  })
}

Now to create a doctor.

// Create a doctor
const drFeelGood = Object.create(DoctorOffice.Doctor)  
drFeelGood.init()  
drFeelGood.name = "Vince Neal"  
drFeelGood.specialty = "Proctology"

console.log(drFeelGood)  

Patient Object

Time to define the patient object in its own block module.

{
  // WeakMap to store private properties of the patient object
  const instanceStorage = new WeakMap();    

  // Function to return the object instance from the WeakMap
  const PRIVATE = function (object) {
    if (!instanceStorage.has(object))
        instanceStorage.set(object, Object.create(null));
    return instanceStorage.get(object);
  }

  // Create the DoctorOffice.Patient namespace
  DoctorOffice.Patient = Object.create({}, {
    // Initializer function
    init: {
      value: function (ssn) {
        PRIVATE(this).name = ""
        PRIVATE(this).dob = ""

        // Social can only, and must, be set on initialization
        PRIVATE(this).ssn = ssn
      }
    },
    // Accessor without a mutator for read-only property
    socialSecurityNumber: {
      get: function () {
        return PRIVATE(this).ssn
      }
    },
    /*
      Public scope property that get/set a private property
      stored in the WeakMap
    */
    dateOfBirth: {
        get: function () {
            return PRIVATE(this).dob
        },
        set: function (d) {
          if (d !== "") PRIVATE(this).dob = d
        },
        enumerable: true
    },
    /*
      Public scope property that get/set a private property
      stored in the WeakMap
    */
    name: {
        get: function () {
            return PRIVATE(this).name
        },
        set: function (n) {
          if (n !== "") PRIVATE(this).name = n
        },
        enumerable: true
    },
  })
}

Try It Out For Yourself

You can try this out by copying my blockModules.js Gist directly into Chrome or Firefox console.

Blog Logo

Steve Brownlee

Head Coach at Nashville Software School. Evolving software development education.


Published