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.