JavaScript has grown to become one of the top trending languages across software development today. Javascript itself can be used in a handful of fields: from frontend to backend web development, mobile application development, desktop apps and even embedded devices. Since its original creation and several upgrades later, Javascript has continued to be an extremely flexible and open language. As a result of this, there are many pros and cons when it comes to using this programming language. That’s where Javascript design patterns come into play.
We reached out to expert Javascript developer, Duc Filan to help us navigate patterns commonly used in this programming language. In this blog post, Duc introduces us to several patterns that can commonly be used to overcome these “cons.”
But first, why is it important to learn how to overcome these issues? Many developers claim that all they have to do is write the language and voila, there you go. Unfortunately, as a result of many product’s constant updates, this is not the case. With today’s fast-paced industry and new developments being turned out every day the term, “Write One, Run Forever,” no longer applies. It’s important to keep in mind that your colleagues and even yourself will have to dive back into your code every now and then to adapt it to new updates and requirements.
But before we dive into patterns and snippets, it’s important to understand some of these concepts below.
1. Basic + Advanced concepts
-
Closure
A closure in Javascript is the ability of a function to manipulate its lexical scope (where it is defined) even when it is called on outside its lexical scope.
Remember that the closure of a function is created when it is defined, not when it is executed. Also, the closure remembers variables in its scope by reference, not by value. So when the value is changed, it will be reflected inside the function.
-
IIFE (Immediately Invoked Function Expression)
As mentioned in Mozilla docs:
IIFE (Immediately Invoked Function Expression) is a JavaScript function that runs as soon as it is defined.
It is a design pattern which is also known as Self-Executing Anonymous Function and contains two major parts. The first is the anonymous function with lexical scope enclosed within the Grouping Operator (). This prevents accessing variables within the IIFE idiom as well as polluting the global scope.
The second part is creating the immediately executing function expression (), through which the JavaScript engine will directly interpret the function.
By using IIFE, we can create a separate execution context to prevent any effects on the global context. We will revisit this topic later.
-
Callback
In Javascript, apart from primitive types, everything else is an object including function. A callback (also known as a callback function), is a function that is passed on to another function as a normal object argument.
-
Hoisting
Let’s take a look at this code snippet:
-
var a = 1; console.log(a + " " + b + " " + c); var b = 2; var c = 3;
In many languages, this will cause errors since b and c are not declared as being used. But thanks to Javascript’s flexibility, this is not an issue. This will output “1 undefined undefined.” This can be identified as hoisting. See how the above code was translated using hoisting:
-
var a = 1; var b; var c; console.log(a + " " + b + " " + c); b = 2; c = 3;
Hoisting means, all declarations (including variable and function) will be hoisted to the top of the code before any manipulation and all the assignments are left where they are (as the above translated code with b and c).
-
Currying
Currying is the process of transforming a function to another function with predefined parameters. Let’s take a look at this code snippet:
-
var multiply = function(a, b) { return a * b; } var square = function(a) { return multiply(a, a); }
Someone might say: “I can use bind with the first argument to be null in order to achieve currying.” Yes, you technically can but bind is not intended to be used in this way. This will later on lead to error-filled code in your program. Your colleague or maybe yourself after one month will inevitably encounter several problems down the road.
-
Memoization
Please note that there is intended to be no ‘r’ in this word. It can be confusing at first glance.
Memoization is the process of caching the result of expansive operations and using it later. Think of this as a classic Fibonacci sequence:
The Fibonacci sequence is a series of numbers where a number is found by adding up the two numbers before it. Starting with 0 and 1, the sequence goes 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, and so forth. Written as a rule, the expression is xn = xn-1 + xn-2.
In this example, xn = xn-1 + xn-2, means xn-1 = xn-2 + xn-3, you can see the repeat of xn-2 and it can be an expansive operation on its chain, and caching it using memoization reduces the execution time quite a lot. You can find out more about how memoization is implemented by accessing my Github repository here.
That’s enough of these boring (but important) concepts, right? Let’s dive into something more interesting!
2. Module Pattern
Duc uses this pattern almost every time he works with Javascript. This pattern helps your Javascript code to be more organized and encapsulated. See more details below:
-
var ModuleName = (function() { // Your code. })();
By using IIFE, we create a new execution context for our code in order to keep some information private and reveal what we want to be viewed by the public eye. In summary, what is used inside the anonymous function will be private to global context and what is returned from the IIFE call is public to global context. This is how your Javascript code can be organized (into specific modules) and encapsulated (using the privacy feature).
Let’s dive deeper into its implementation here
2.1. Private methods and properties
This pattern make use of the privacy of execution context to hide the private methods. Look at the below example:
-
// Authentication module is pre-defined under name: authenticationModule. var CarEngineOperatorModule = (function(authenticationModule) { // ============================================================ // Other parts of the code. // ============================================================ var _ran_Distance_km = 1824250; var _isAuthenticated = function(userKey) { return authenticationModule.isAuthenticated(userKey); }; var _fillFuel = function() { // Filling fuel operations. }; var startCarSystem = function(userKey) { if !_isAuthenticated(userKey) return; _fillFuel(); // Other operations. }; var getRanDistance_km = function() { if !_isAuthenticated(userKey) return; return _ran_Distance_km; } // ============================================================== // Other parts of the code. // ============================================================== return { startCarSystem: startCarSystem, getRanDistance_km: getRanDistance_km // Other public methods. }; })(authenticationModule);
In this example, we illustrate the process of a self-driving car using the driver’s voice commands.
To start the car, the driver says to the car: “Okay car!” (Like “Okay Google!”). The voice command receiving module will hear the command, filter it and respond with the car engine operator module (CarEngineOperatorModule) to start the car. In this case, the start method of the operator module (startCarSystem()) will be public to the global context, leaving behind operations private.
If some module attempts to call the private methods, for example _fillFuel(), there will be an error. This error is an intended action because we don’t want any module to manipulate a dangerous method out of our control (like __fillFuel())
A similar concept is applied to private properties. We will hide properties that can only be used inside the module or ones that can only be obtained under specific circumstances. In this case, the number of how many kilometers the car traveled is considered personal information, and we wouldn’t want anyone to be able to easily access this information. So we will hide it from the outside world.
2.2. Object Chain
We often use this pattern to store settings or constants in a separate area to keep the system organized. Once you make any changes to a setting or a constant or whatever you define, you will find it easier to locate the place you need to change, and be able to change it only once. You won’t have to worry about other positions using your settings. This pattern uses object literal syntax by multiple layers. This is what it looks like:
-
var themes = { water: "Water", forest: "Forest", ocean: "Blue Ocean" }; var supporters = { male: "Male", female: "Female" } var constants = { setting: { debugMode: false, theme: themes.ocean, supporter: supporters.female }, message: { error: { failRequest: "Request failed! Try again later.", authenticationFail: "You're not authenticated, not able to access this area!" }, warning: { delete: "You're deleting {1}. This process cannot be undone!", grantPermission: "You're granting {1} permission to the user named {2}" } } }
This pattern takes advantage of the suggestion feature of IDEs. When you type “constants.” a list of constants will be shown for you to quickly choose the one that you need.
2.3. Facade pattern
If you’re working with Javascript, you will most likely use some of the libraries already available. In most cases, we only use part of the library.
In order to focus on what you work with and simplify the usage of the library, try to think of it as how API is implemented for other developers. It provides simplicity to whoever chooses to use it and it hides its underlying complexities. Let’s take a look at this pattern in action.
Here is this repository for more info.
-
var authFacade = (function() { var _totp = new OTPAuth.TOTP({ issuer: 'ACME', label: 'AzureDiamond', algorithm: 'SHA1', digits: 6, period: 30 }); return { get2FAToken: function(callback) { storageFacade.getSecretCode(function(secretCode) { if (!secretCode) return; _totp.secret = OTPAuth.Secret.fromB32(secretCode); callback(_totp.generate()); }); } } })();
This authFacade module simplifies the OTPAuth library, by providing the get2FAToken facade, whoever chooses to use it simply has to pass the callback function in order work with it.
-
authFacade.get2FAToke(callback);
The rest of the work is hidden.
And there you have it. The most common patterns of this complex and unique programming language. Of course, every developer has their own opinion of which are the most common patterns and of which ones they prefer when it comes to using Javascript. What’s most important is that you identify which patterns work best for your project goals to better increase the chances of running into any errors down the line!