There are a variety of common situations and obstacles programmers deal with. Over time many design patterns emerged that nicely addressed these common issues. Of these, there are three patterns that JavaScript developers employ more often than others.
Three of the patterns common in JS:
Module Pattern
Objects that a developer wishes to keep private can be indicated with an underscore. Look at an account data Object, for example. This would likely house sensitive data. The caution needed when handling this object is often relayed with an underscore at the beginning of the name like this:
In JavaScript, classes can work as the module as they protect information from being accessed through encapsulation. In general, the idea is to invoke the module immediately through a process known as
Invoking a function immediately can be accomplished the same way as any function; that is to use ( ). In this case, the function has no name and passes another anonymous function as an argument, and then is invoked right after declaring. It looks like this: (function() { ...code })()
Note: IIFE only works because () is an expression. This will not work on function declarations like this: function myFunctName() { ...code }
Basic Structure
This is wrapped in (), returns public data and invokes immediately using the () right before closing.
const myObject = (function() {
// ...here is where private data variables can live
return {
// ...public methods and properties reside here
};
// NOTE: This contains a () in the closing below. This invokes this function immediately
}());
↓Example ↓
const account = (function() {
// Private data
let balance = 1000;
return {
// Public data
name: "John",
getBalance: function() {
return balance // balance is available as this function reside within the function scope.
},
addINterest: function(interest) {
balance += interest;
}
};
}());
console.log(account.name); // RESULT: "John"
console.log(account.getBalance()); // RESULT: 1000
account.balance = 2000; // RESULT: balance can not be changed as it is locked within the functions's scope.
console.log(account.getBalance()); // RESULT: 1000 because it could not be changed.
console.log(account.addINterest(300)); // RESULT: 300 is added to balance because addINterest is inside of the function scope.
console.log(account.getBalance()); // RESULT: 1300 because this was changed with addINterest()
Revealing Module Pattern
Below is the general structure. This is wrapped in (), returns public data and invokes immediately using the () right before closing.
const myObject = (function() {
// ...here is where private data variables can live
privateVar = data;
functionName1() {
...
};
functionName2() {
...
};
return {
// ...public methods and properties reside here
// The functions detailed in the private area can be made public via the return
functionName1: functionName1,
functionName2: functionName2
};
// NOTE: This still contains a () in the closing to invoke this function immediately
}());
↓ Example ↓
const account = (function() {
// Private data
let balance = 1000;
function getBalance() {
return balance // balance is available as this function reside within the function scope.
};
function addINterest(interest) {
balance += interest;
};
return {
// Public data
name: "John",
getBalance: getBalance,
addINterest: addINterest
};
}());
console.log(account.name); // RESULT: "John"
console.log(account.getBalance()); // RESULT: 1000
account.balance = 2000; // RESULT: balance can not be changed as it is locked within the functions's scope.
console.log(account.getBalance()); // RESULT: 1000 because it could not be changed.
console.log(account.addINterest(300)); // RESULT: 300 is added to balance because addINterest is inside of the function scope.
console.log(account.getBalance()); // RESULT: 1300 because this was changed with addINterest()
A great in-depth look at Module Pattern can be found here ->
Prototype Pattern
In situation where large database queries or similar operations are needed multiple times, there can be a tremendous performance boost to make a copy of the original instead of running the whole process again. This is what the Prototype Pattern attempts to accomplish. Ultimately, a shallow clone is made of the original Object. To ensure the variables and methods bind to the object's structure, the keyword
var appleSauce= function() {
this.apples = 4;
this.tool = 'masher';
this.spices= 'cinnamon';
}
appleSauce.prototype = {
mash: function() {
// Mash the apples
},
mix: function() {
// Mix in the spices
}
}
Revealing Prototype Pattern
This variation is similar to the Prototype Pattern described above, but different in that the public and private code can be kept together
var appleSauce= function() {
this.apples = 4;
this.tool = 'masher';
this.spices= 'cinnamon';
}
appleSauce.prototype = {
const mash = function() {
// Mash the apples
},
const mix = function() {
// Mix in the spices
}
return {
mashTheApples : mash ,
mixInSpices: mix
}
}();
Observer Pattern
There are times when something changes prompting the need for a change elsewhere. The
The following are required on the Subject class:
- observers property– This holds an array of observers.
- addObserver() method – Pushes an observer onto the observer’s array.
- removeObserver() method – Removes an observer from the observer’s array.
- notify() method – Lets all of the observers know that there has been a change.
var Subject = function() {
this.observers = [_];
return {
subscribeObserver: function(observer) {
this.observers.push(observer);
},
unsubscribeObserver: function(observer) {
var index = this.observers.indexOf(observer);
if(index > -1) {
this.observers.splice(index, 1);
}
},
notifyObserver: function(observer) {
var index = this.observers.indexOf(observer);
if(index > -1) {
this.observers[index].notify(index);
}
},
notifyAllObservers: function() {
for(var i = 0; i < this.observers.length; i++){
this.observers[i].notify(i);
};
}
};
};
var Observer = function() {
return {
notify: function(index) {
console.log("Observer " + index + " is notified!");
}
}
}
var subject = new Subject();
var observer1 = new Observer();
var observer2 = new Observer();
var observer3 = new Observer();
var observer4 = new Observer();
subject.subscribeObserver(observer1);
subject.subscribeObserver(observer2);
subject.subscribeObserver(observer3);
subject.subscribeObserver(observer4);
subject.notifyObserver(observer2); // Observer 2 is notified!
subject.notifyAllObservers();
// Observer 1 is notified!
// Observer 2 is notified!
// Observer 3 is notified!
// Observer 4 is notified!