ref – https://blog.bitsrc.io/understanding-design-patterns-in-javascript-13345223f2dd
export import
First let’s take a look how we export our implementations.
When you put keyword export in front, it means we’re exposing it for others to use.
Any other objects that do not have this keyword will not be exposed. Thus giving it privacy.
utils.js
1 2 3 4 5 6 7 8 9 10 11 12 |
export const STRING_LABEL_ONE = "testing 1 2 3"; export function myRand() { return (rand() > 0.5) ? randomFunc1() : randomFunc2(); } function randomFunc1() {..} function randomFunc2() {..} function privateLog() { console.log('Private Function'); } |
Another to simply export all of them together.
utils.js
1 2 3 4 5 6 7 8 9 10 11 12 13 |
const STRING_LABEL_ONE = "testing 1 2 3"; function myRand() { return (rand() > 0.5) ? randomFunc1() : randomFunc2(); } function randomFunc1() {..} function randomFunc2() {..} function privateLog() { console.log('Private Function'); } export { myRand, STRING_LABEL_ONE }; |
In order to use them, we use import in two ways:
one at a time
1 |
import { myRand, STRING_LABEL_ONE } from './utils.js'; |
or just import the whole module.
1 |
import * as utils from './utils.js'; |
renaming an import export
renaming an export
1 2 3 |
const thoughts = 'I want to go swimming'; export {thoughts as demands}; |
renaming an import
1 2 3 |
import { add, multiply as mult } from './utils.js'; console.log(add(3, 7)); console.log(mult(3, 7)); |
Singleton Pattern
A Singleton is an object which can only be instantiated only once. A singleton pattern creates a new instance of a class if one doesn’t exist. If an instance exists, it simply returns a reference to that object. Any repeated calls to the constructor would always fetch the same object.
JavaScript has always had singletons built-in to the language. We just don’t call them singletons, we call them object literal. For example:
1 2 3 4 5 6 7 8 |
const user = { name: 'Peter', age: 25, job: 'Teacher', greet: function() { console.log('Hello!'); } }; |
Because each object in JavaScript occupies a unique memory location and when we call the user object, we are essentially returning reference to this object.
If we try to copy the user variable into another variable and modify that variable. For example:
1 2 |
const user1 = user; user1.name = 'Mark'; |
We would see both of objects are modified because objects in JavaScript are passed by reference not by value. So there is only a single object in the memory. For example:
1 2 3 4 5 6 |
// prints 'Mark' console.log(user.name); // prints 'Mark' console.log(user1.name); // prints true console.log(user === user1); |
Singleton pattern can be implemented using the constructor function
So originally, in js, we declare a function like so:
1 2 3 4 5 |
function Person(newName) { console.log('entered person'); console.log(this); this.name = newName; } |
This function is an object. It has a this that points to the global environment (es5), or null (es6).
Hence if you were to call it like so:
1 |
let joy = Person('Joy'); |
it would simply execute the function implementation and log the statements. As for this, in es6, it would be null and if you try to access property name on null, it will crash.
Or if you’re in es5, it’ll get the Window object and attach the property name on there.
You are working on the Window object, and not an object of your own.
Therefore, you have two choices. You’re either running the function as a group of executions, or you’re manipulating the global window object (es5), or simply running into access errors because ‘this’ is null in es6.
In order to instantiate your own object, you need to use new. When you’re using ‘new’, it will create an instance of an object for you. Then point the this of the function onto the object literal. Then, your joy variable reference will be pointing to that object instance.
1 |
let joy = new Person('Joooy'); |
In your function implementation, your ‘this.name’ will have that object instance add a property name. Then initialize it to the newName parameter, and so forth.
Now, in our case, we want a singleton. So under this environment, the key idea is that we let its normal workings of ‘this’ point to an object instance do its thing. The only difference is, we need to use a global variable to keep count and maintain the singleness of this singleton.
We do so by using this logic:
1 2 3 4 5 6 7 8 |
let instance = null if (instance) { return instance; } instance = this; |
Basically we declare a variable reference.
If it’s null, we point it to the function object.
If the instantiation of the function object is used again, we check for the instance.
If that instance is already pointing to a function object, we return the same instance.
Thus, this ensures singleness.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
let instance = null; // declare a reference function User() { // declare a function // if the reference is pointing to an object, we just return it if(instance) { return instance; } // have our reference point to the function object itself. // now, whatever properties we manipulate, it will be reflected in instance instance = this; this.name = 'Peter'; this.age = 25; return instance; } // here we try to instantiate an object. // but because instance is undefined, it'll be pointed to // User function object's this. It then initializes the properties // and return the object to be pointed by user1. const user1 = new User(); // here, we try to instantiate a User object. However, since instance // is already pointing to User function object, it just returns. const user2 = new User(); // hence, they are referencing the same object console.log(user1 === user2); // true |
Singleton using Module Pattern
Can also use module pattern to implement the singleton. But we must conform to the rules of the module pattern.
The rules is that it returns an object that exposes properties for others to use.
Thus, everyone else will be call for an exposed property (getInstance) that will only give them that same single literal object.
In order to do this, we use the same concept of an instance to checker whether it’s already pointing to an object.
1) We declare the instance variable.
2) When others call that exposed property, we which checks to see if it’s already pointing to an object.
3) If it is, simply return our instance. If it’s not, we return an object literal.
This way, we ensure singleness.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
const singleton = (function() { let instance; return { getInstance: function() { if(!instance) { instance = { name: 'Peter', age: 24, }; } return instance; } } })(); const instanceA = singleton.getInstance(); const instanceB = singleton.getInstance(); // prints true console.log(instanceA === instanceB); |