Constructor pattern
It is a bit different from other languages because we are not going to use classes for this (although they are available from ES6). So the main goal here is to create a new object with their own object scope. We are going to start taking a look at what the new
operator is doing for us
- If you put this operator in front of an object (remember functions are objects in javascript) it will give us a new object.
- Link the created object to an object prototype (it is not inheritance btw)
- Binds
this
keyword to the scope of the new object - Returns the previous
this
(for more information you can visit new operator)
An example:
function Todo(title, isDone) {
this.title = title;
this.isDone = isDone;
// here should be placed this, but new is doing this for us
}
So this pattern allows us to pass some parameters to a function (which is the name of our object), and because we are going to use new
, this
is available in our scope.
Let's take a look at how to use the constructor
// wrong way
a = Todo('title', false) // a will be undefined
console.log(a.title) // because a is undefined you can't access to a
// right way
a = new Todo('title', false) // a will be the object with this in context
console.log(a.title) // will print title
Factory pattern
It is one of the core patterns which allows it to create objects for you. The main purpose of this one is to hide the logic about object creation for some clients.
So, let's say that now we have personal and work Todo
s, and the difference is personal
ones are done or not, and work
ones can measure the progress.
function PersonalTodo(config) {
this.title = config.title;
this.isDone = config.isDone;
this.complete = function () {
this.isDone = true;
}
this.reset = function () {
this.isDone = false;
}
}
function WorkTodo(config) {
this.title = config.title;
this.progress = 0;
this.addProgress = function() {
if(this.progress > 90) return;
this.progress += 10;
}
this.substractProgress = function() {
if(this.progress < 10) return;
this.progress -= 10;
}
}
Now we can create our on TodoFactory
function TodoFactory() {
this.createTodo = function(config) {
if(config.type === 'personal') {
this.todoType = PersonalTodo;
}
if(config.type === 'work') {
this.todoType = WorkTodo;
}
return new this.todoType(config)
}
}
Let's give it a try:
var myFactory = new TodoFactory();
var personal = myFactory.createTodo({type: 'personal', title: 'foo', isDone: false})
console.log(personal instanceof PersonalTodo); // true
console.log(personal) // personal todo with title: "foo" isDone: false
var work = myFactory.createTodo({type: 'work', title: 'foo'})
console.log(work instanceof WorkTodo); // true
console.log(work) // work todo with title: "foo" progress: 0
work.addProgress()
console.log(work) // work todo with title: "foo" progress: 10
What we can see here is a way to hide object creation (using explicitly new
) for bringing to life new objects.
Singleton pattern
The main goal is to have in our application only one instance of an object. From the pattern, you usually will have a method in your object called getInstance
to get the singleton instance and here you will check if the instance exists or you need to create one if it does not.
var TodoSingleton = (function() {
var instance;
function init() {
var instance = new WorkTodo({title: 'workTodo'});
return instance;
}
return {
getInstance: function () {
if(!instance) {
instance = init();
}
return instance;
}
}
})();
Now, let's give it a try:
work1 = TodoSingleton.getInstance() // here we create a new WorkTodo
work2 = TodoSingleton.getInstance() // here we use the previous instance
console.log(work1.progress) // 0
console.log(work2.progress) // 0
work1.addProgress()
console.log(work1.progress) // 10
console.log(work2.progress) // 10
This pattern is very useful when you have to lead with services in your application. Particularly, you don't want to have different instances of one of them running in your app. Also, sometimes you want to load a third party lib in your app, which you want to avoid loading it every time you want to use it.
Extra
<button id="status"> Get Status </button>
function PersonalTodo(config) {
this.title = config.title;
this.isDone = config.isDone;
this.complete = function () {
this.isDone = true;
}
this.reset = function () {
this.isDone = false;
}
this.getStatus = function() {
return this.isDone;
}
}
var personal = new PersonalTodo({title: 'foo', isDone: false})
personal.getStatus() // false
var element = document.getElementById("status");
element.onclick = personal.getStatus; // does not work this is in context of dom
element.onclick = personal.getStatus.bind(personal); // now you bind this to the object
How can we avoid using bind
?
// Solution 1
function PersonalTodo(config) {
this.title = config.title;
this.isDone = config.isDone;
this.complete = function () {
this.isDone = true;
}
this.reset = function () {
this.isDone = false;
}
return {
getStatus: function() {
return alert(this.isDone);
}
}
}
var element = document.getElementById("status");
element.onclick = personal.getStatus; // not getStatus will be in the context of the object
NOTE: The previous sample can be replaced using an arrow function from ES6.
Conclusion:
Design patterns are very useful while developing. Having the power of recognizing them can speed up your coding and solving common problems. Another advantage I see and appreciate is the fact you can easily understand new codebases and potentially learn new technologies faster because all software engineers base their programs on this basic and powerful stuff.
Top comments (0)