JavaScript Patterns [Ch.2 | Part 1: Essentials]

JavaScript Patterns [Ch.2 | Part 1: Essentials]

So in the previous article, we talked about chapter 1 of Introduction which discussed patterns concepts in detail, also we discussed different essential concepts related to JavaScript which provides a context for the following chapters.

This article discusses Chapter 2 titled Essentials in which we will discuss essential best practices, and habits for writing high-quality JavaScript code, such as avoiding globals, preaching length in loops, following coding conventions, and more.

This chapter also includes habits more about the overall code creation process, these habits and best practices can help you write better, more understandable, and maintainable code—code to be proud of.

👷‍♂️ Writing Maintainable Code

Bugs are COSTLY to fix. And their cost increases over time, It’s best if you can fix a bug right away, Otherwise, you move on to other tasks and forget all about that particular code.

Revisiting the code after some time has passed requires:

  • Time to relearn and understand the problem
  • Time to understand the code that is supposed to solve the problem

🏢 Maintain code in large companies

Another problem, specific to bigger companies, the person who fixes the bug is not the same person who created the bug (and also not the same person who found the bug)

It’s therefore critical to reduce the time it takes to understand code, either written by yourself some time ago or written by another developer in the team

👌 Criterias of Maintainable code

Maintainable code means code that:

  • It has to be readable
  • It has to be predictable
  • It has to be consistent
  • It has to be documented
  • Looks as if it was written by the same person

The rest of this chapter addresses these points when it comes to writing JavaScript

🌍 Minimizing Globals

A variable declared inside of a function is local to that function and not available outside the function, On the other hand, global variables are those declared outside of any function or simply used without being declared

🔌 Environment global object

Every JavaScript environment has a global object accessible when you use this outside of any function.

In browsers, there is a property called window that (usually) points to the global object itself, the following snippet shows how to create and access a global variable in a browser environment:

myglobal = "hello"; // antipattern, do not use

console.log(myglobal); // "hello"
console.log(window.myglobal); // "hello"
console.log(window["myglobal"]); // "hello"
console.log(this.myglobal); // "hello

🚧 The Problem with Globals

The main problem with global variables is that they are shared among all the code in your JavaScript application and live in the same global namespace

Because of that, there is always a chance of naming collisions—when two separate parts of an application define global variables with the same name

Besides that, It’s also common for web pages to include code not written by the developers of the page, for examples:

  • A third-party JavaScript library
  • Scripts from an advertising partner
  • Code from a third-party user tracking and analytics script

🏡 Be a Good neighbor to scripts

It’s important to be a good neighbor to the other scripts that may be in the same page and use as few global variables as possible

Later in the next chapters, we will learn more about strategies to minimize the number of globals such as namespaces, self-executing immediate functions (IIFE) but the most important pattern is to always use the JS keywords var, let and const

Yet another reason to avoid globals. If you want your code to run in different environments (hosts), it’s dangerous to use globals because you can accidentally overwrite a host object

👣 Creating Global variables Involuntarily

It is easy to create globals involuntarily because of two JavaScript features. First, you can use variables without even declaring them

And second, JavaScript has the notion of implied globals, meaning that any variable you don’t declare becomes a property of the global object

Consider the following example:

function sum(x, y) {
  // antipattern: implied global, do not use
  result = x + y;
  return result;
}

In this code, result is used without being declared. The code works fine, but after calling the function you end up with one more variable result in the global namespace that can be a source of problems

⛓ Chain assignments

Another antipattern that creates implied globals is to chain assignments as part of a keyword(var, let and const) declaration

function o() {
  let a = b = 10;


  console.log(this.a);  // undefined
  console.log(this.b);  // 10
}

In the above snippet, a is local but b becomes global, which is probably not what you meant to do 😢

If you wonder why that happens, it’s because of the right-to-left evaluation

First, the expression b = 0 is evaluated and in this case, b is not declared, The return value of this expression is 0 and it’s assigned to the new local variable declared with let a

In other words, it’s as if you’ve typed: let a = (b = 0);

If you’ve already declared the variables, chaining assignments is fine and doesn’t create unexpected globals. Example:

function foo() {
  let a, b;
  // ...
  a = b = 0; // both local
}

Group 63.png

🆚 Globals Vs. Implied Globals

There’s one difference between implied globals and explicitly defined ones, mainly the difference is in the ability to undefine these variables using the delete operator:

// define three globals
var global_var = 1;
global_novar = 2; // antipattern

(function () {
  global_fromfunc = 3; // antipattern
}());

// attempt to delete
delete global_var; // false
delete global_novar; // true
delete global_fromfunc; // true

// test the deletion
typeof global_var; // "number"
typeof global_novar; // "undefined"
typeof global_fromfunc; // "undefined"

🍀 NOTE

In ES5 strict mode, assignments to undeclared variables (such as the two antipatterns in the preceding snippet) will throw an error.

💍 Single var Pattern

The book discusses the benefits of using a Single var pattern which is simply using a single var statement with a comma-separated list of variable declarations and assignments whenever possible like the following snippet:

function func() {
  var a = 1,
    b = 2,
    sum = a + b,
    myobject = {},
    i,
    j;

  // function body...
}

Also, it mentioned that it has a lot of benefits like:

  • Provides a single place to look for all the local variables
  • Prevents logical errors when a variable is used before it’s defined
  • Helps you remember to declare variables and therefore minimize globals

But actually, the world has moved and there is a lot of changes, So I suggest you read the following article titled Multiple var statements in JavaScript, not superfluous which has a more valuable/updated opinion about the pattern of single var

🧨 Hoisting: A Problem with Scattered vars

JavaScript enables us to have multiple var statements anywhere in a function, they all act as if the variables were declared at the top of the function

This behavior is known as hoisting, This can lead to logical errors when you use a variable and then you declare it further in the function

For example, in the following code snippet:

// antipattern
myname = 'global'; // global variable
function func() {
  alert(myname); // "undefined"
  var myname = 'local';
  alert(myname); // "local"
}
func();

We might expect that the first alert() will prompt “global”, and the second will prompt “local.”

It’s a reasonable expectation because at the time of the first alert, myname was not declared and therefore the function should probably “see” the global myname

But that’s not how it works, The first alert will say “undefined” because myname is considered declared as a local variable at the top of the function, Therefore to avoid this type of confusion