Learning a language programming is pretty simple. People think it’s hard just because they don’t really understand the “way of coding”. We might find that we really understand all of syntaxes but why we still feel hard to read a source code? Because the reader and writer not having the same thoughts, level of software design. Talking about levels I mean we are lacking of knowledge about software design, especially the design patterns in coding. For instance, if we are familiar with Object Oriented Programming, we might find hard to read javascript at first glance. It doesn’t have the class concept. But why? How can it be encapsulated? If we learned and spent time to practise S.O.L.I.D on daily basic we will be aware of these principles in everywhere.
Let’s get back to the question “How can it be encapsulated?”. That is the module pattern.
Module Pattern
In fact, module in javascript is like class in OOP.
function() {
// private context
return {
// public context
}
}
For more clear.
function handler(client, chatroomManager) {
function handleRegister() {}
function handleJoin() {}
return {
handleRegister,
handleJoin
}
}
We use delegate pattern a lot in javascript. Delegate is a useful pattern in order to make code loose connected. We also can declare the private methods in side module.
function handler(client, chatroomManager) {
const socket = client;
return {};
}
Prototype Pattern
When I compare with ruby on rails, I see that it’s like the meta-programming class_eval. It allows us to add extra attributes to existing object constructor – constructor function, “class” in javascript, modelling objects. It’s necessary to say that we use it to add “constructor attributes”, not object’s attributes.
Incorrect.
function Person(first, last) {
this.firstName = first;
this.lastName = last;
}
Person.nationality = "English";
var myFather = new Person("John", "Doe");
"The nationality of my father is " + myFather.nationality;
// "The nationality of my father is undefined"
Correct.
function Person(first, last) {
this.firstName = first;
this.lastName = last;
}
Person.prototype.nationality = "English";
var myFather = new Person("John", "Doe");
"The nationality of my father is " + myFather.nationality;
// "The nationality of my father is English"
Above “nationality” is from constructor. If we print out the myFather object, we will see that.

The result is still the same if we add value to prototype after object had been created.
function Person(first, last) {
this.firstName = first;
this.lastName = last;
}
// Person.prototype.nationality = "English";
var myFather = new Person("John", "Doe");
Person.prototype.nationality = "English";
"The nationality of my father is " + myFather.nationality;
// "The nationality of my father is English"
This is normal way to declare an attribute of object (we need to distinguish it from prototype). The extra attributes added ONLY belong to the object which they were added. It’s like the singleton properties.
function Person(first, last) {
this.firstName = first;
this.lastName = last;
}
// Person.prototype.nationality = "English";
var myFather = new Person("John", "Doe");
myFather.nationality = "Vietnam";
"The nationality of my father is " + myFather.nationality;
// "The nationality of my father is Vietnam"
Initializing.
function CustomCropper(element) {
this.element = element || {
cover: '.cover',
coverImage: '#cover__image'
};
this.cropper = function() {
var image = $(this.element.coverImage);
image.cropper({
checkImageOrigin: false,
viewMode: 3,
background: false,
checkCrossOrigin: false,
minCropBoxWidth: $(this.element.cover).width(),
minCropBoxHeight: $(this.element.cover).height()
});
return image.data('cropper');
};
// CALL THIS FUNC DIRECTLY
this.cropper();
}
Object Literals
The objects described by comma-saperated name/value.
var person = {
name: "Will Nguyen",
age: 28,
title: "Software Engineer",
favoriteQuote: function(q) {
console.log(q);
}
};
person.favoriteQuote("Nothing special");
Singleton Pattern
function WillSingleton() {
var instance;
function Singleton(options){
options = options || {};
this.dancing = options.dancing || "No Style";
}
return {
instance: function(options) {
if (instance === undefined){
instance = new Singleton(options);
}
return instance;
}
}
}
console.log(WillSingleton().instance().dancing);
// No Style
We make sure there is only one instance.
Inheritance
We can use prototype like above. Another way is using call method.
function Brick() {
this.width = 10;
this.height = 20;
}
function BlueGlassBrick() {
Brick.call(this);
this.opacity = 0.5;
this.color = 'blue';
}
(new BlueGlassBrick()).width;
// 10
Involving call method will append the properties to this. Thus, be careful we might override them.
function Brick() {
this.width = 10;
this.height = 20;
}
function BlueGlassBrick() {
Brick.call(this);
this.width = 0.5;
this.color = 'blue';
}
(new BlueGlassBrick()).width;
// 0.5
Observer
We want to handle a list of objects and notify them all about something like any changes on list.
Firstly, we implement a list of observers.
function ObserverList(){
this.observerList = [];
}
ObserverList.prototype.add = function( obj ){
return this.observerList.push( obj );
};
ObserverList.prototype.count = function(){
return this.observerList.length;
};
ObserverList.prototype.get = function( index ){
if( index > -1 && index < this.observerList.length ){
return this.observerList[ index ];
}
};
ObserverList.prototype.indexOf = function( obj, startIndex ){
var i = startIndex;
while( i < this.observerList.length ){
if( this.observerList[i] === obj ){
return i;
}
i++;
}
return -1;
};
ObserverList.prototype.removeAt = function( index ){
this.observerList.splice( index, 1 );
};
We use prototype here because we expect that these function such as “add, count, get” are changeable. Applying a design pattern.
Separating what change from what stay the same.
Thus, it’d be flexible. What we put in constructor should not be changed. Any object created by ObserverList will have a certain attributes in constructor.
Secondly, we implement the subject to observe them, broadcast messages for example.
function Subject(){
this.observers = new ObserverList();
}
Subject.prototype.addObserver = function( observer ){
this.observers.add( observer );
};
Subject.prototype.removeObserver = function( observer ){
this.observers.removeAt( this.observers.indexOf( observer, 0 ) );
};
Subject.prototype.notify = function( context ){
var observerCount = this.observers.count();
for(var i=0; i < observerCount; i++){
this.observers.get(i).update( context );
}
};
We will override update function later, depending on what we want to do with these observers.
function Observer(){
this.update = function(){
// ...
};
}