Categories
JavaScript Best Practices

Maintainable JavaScript — IIFEs and Events

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at avoiding global variables and loosen coupling in our UI.

No Global Approach

We can create IIFEs to create private variables without any global variables.

We pass the global objects we need into the IIFE and we can use it without changing it.

For instance, we can write:

(function(win) {
  const doc = win.document;
  // ...
}(window));

We create our IIFE and then called with the window object so that we can access window.document and assign it to a variable.

Then we can do whatever we want with it.

The function wrapper can be used for scripts where we don’t want to create any global objects.

This pattern is of limited use.

Any scripts that are used by other scripts on the page can’t use this approach.

If we have small scripts that don’t interact with any other scripts, then this approach can be used.

Event Handling

Event handling is doing all the time in client-side JavaScript.

Our app has to handle events from inputs, mouse clicks, scrolling and more.

There are also lots of potentials to create tightly coupled code if we aren’t careful.

The event object is available in the event handler functions as a parameter.

And we can easily use it to create tightly coupled code.

To make sure we don’t create tightly coupled code, we should keep a few things in mind.

Separate Application Logic

We need to separate app logic so that we don’t create app logic code that’s coupled to the UI.

App logic code that’s in the event handlers are hard to trace and test.

Also, we may end up duplicating our logic code when we can make them reusable.

And testing is harder is we create logic code that’s coupled to the event handling code.

We’ve to fire events to test our app logic code even when we just want to test the logic.

Therefore, we should separate the app logic code from the event handling code.

For instance, we can separate our app logic code from our event handles by writings:

function onClick(event) {
  showPopup(event);
}

function showPopup(event) {
  const popup = document.getElementById("popup");
  popup.style.left = `${event.clientX}px`;
  popup.style.top = `${event.clientY}px`;
  popup.className = "popup";
}

const button = document.querySelector('button');
button.addEventListener('click', onClick);

We have the showPopup function to show the popup and the onClick method to listen to clicks on the button.

When onClick runs showPopup runs so that we don’t have all our logic in our event handler function.

Don’t Pass the Event Object Around

We shouldn’t pass the event object around since it depends on the element that the event handler function is called on.

We want to decouple our app logic from the DOM elements, so we shouldn’t take the event object as a parameter in our app logic function.

Instead of taking the event object in showPopup , we should just take x and y coordinates.

For instance, we can write:

function onclick(event) {
  const {
    clientX,
    clienty
  } = event;
  showPopup(clientX, clienty);
}

function showPopup(x, y) {
  const popup = document.getElementById("popup");
  popup.style.left = `${x}px`;
  popup.style.top = `${y}px`;
  popup.className = "popup";
}

const button = document.querySelector('button');
button.addEventListener('click', onClick);

We changed the showPopup function to take the x and y coordinate of the popup so that we don’t have to pass the event object to our app logic.

This keeps our event handling code loosely coupled from the app logic code.

Conclusion

We should decouple our app logic code from our event handling code.

Also, we can create IIFEs to get global variables and use them inside.

Categories
JavaScript Best Practices

Maintainable JavaScript — Global Variables

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at avoiding global variables.

Avoid Globals

JavaScript’s execution environment is unique in that we used to use lots o global variables and functions.

The default execution environment of JavaScript is to use global variables everywhere.

Everything we have is defined as properties of the global object.

It’s an object that represents the outermost context for a script.

The window is the global object in the browser.

So any variable or function declared in the global scope becomes the property of the window object.

For instance, we have:

var color = "red"

or

function getColor() {  
  console.log(color);  
}

Then we can get the values by using:

console.log(window.color)

or:

window.getColor();

However, it’s a bad practice to create global variables everywhere since they have many issues.

One issue that it has is naming collisions.

Since everything is in the same scope, it’s likely that we have naming collisions within the global scope.

We may have defined the color global variable somewhere else.

This will overwrite the value of the one that’s defined earlier.

The getColor variable depends on color , so it would hard to track down the actual value of color .

We don’t know where color comes from.

Also, we run the risk that the global variable may become a built-in browser global variable later on.

Code Fragility

Having global variables makes the code tightly coupled to the environment.

If the environment changes, then the function is likely to break.

The getColor method logs undefined if the color variable no longer exists.

This means that any change to the global environment is capable of causing errors throughout the code.

Globals can be changed at any point by any function.

This means the reliability of global variables is also suspect.

To make our code more robust, we should avoid global variables.

So we instead of logging color directly in our getColor function, we should log the color from a parameter instead.

For instance, we can write:

function getColor(color) {  
  console.log(color);  
}

to get the color from a parameter instead.

This way, we know where it comes from.

Difficulty Testing

Having global variables also makes our code hard to tests.

Creating tests is very difficult if they rely on global variables since they can be changed by anything.

The tight coupling between different parts of the code caused by global variables causes unpredictable results.

Therefore, we got to fix this by removing the dependency on global variables.

We shouldn’t create our own global variables.

But we can rely on globals that are native to JavaScript like Array or Date .

Conclusion

We should avoid global variables as much as we can.

They make our code hard to test.

And the code that we create is fragile since the global variables can be changed anywhere by anything.

Categories
JavaScript Best Practices

Maintainable JavaScript — Functions and Arrays

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at how to check for functions, arrays, and bad ways to check for object properties.

Detecting Functions

Functions are reference types in JavaScript.

They’re created from the Function constructor of which each function is an instance.

There’s a bad way to check for a function.

We shouldn’t check for functions with the instanceof operator.

For instance, we shouldn’t write:

function func() {}

console.log(func instanceof Function);

to check for a function.

This isn’t good because it doesn’t work across frames since each frame have their own Function instance.

Instead, we should use the typeof operator.

So we should write:

function func() {}

console.log(typeof func === 'function');

typeof works across all frames.

Only IE8 or earlier have limitations with this, so we don’t have to worry about it.

Detecting Arrays

Arrays are also objects, so we can check for arrays with the instanceof operator.

For instance, we can write:

arr instanceof Array

But like any other uses of the instanceof operator, it won’t work across frames.

Another way that was recommended by Douglas Crockford was to check for array methods.

For instance, we can write;

function isArray(value) {
  return typeof value.sort === "function";
}

to check whether the sort method exists in the value object.

But anything can have the sort method so it’s probably not very accurate.

Another suggestion is to call the toString method on the value to see if it returns '[object Array]' .

So we can write:

function isArray(value) {
  return Object.prototype.toString.call(value) === "[object Array]";
}

This works across all frames and only arrays return this string, so this can be used to check for array.

However, now there’s a built-in way to check for arrays with the Array.isArray method.

For instance, we can write:

function isArray(value) {
  if (typeof Array.isArray === "function") {
    return Array.isArray(value);
  } else {
    return Object.prototype.toString.call(value) === "[object Array]";
  }
}

We check if Array.isArray exists by checking whether it’s a function.

Then we can call it to check if it’s an array.

Array.isArray works across all frames, so we can use it to check for arrays anywhere.

This is implemented in IE9, Firefox 4+, Chrome, Opera 10.5+, and Safari 5+.

This means that we can just use Array.isArray and forget about everything else.

Bad Ways of Detecting Properties

Detecting properties is another tricky issue we may run into.

There are a few bad ways to check for properties in an object.

They include:

if (object[propertyName]) {
  //...
}

if (object[propertyName] != null) {
  //...
}

if (object[propertyName] != undefined) {
  //...
}

All of them check for all falsy values, so they won’t work well for our needs.

Falsy values include 0, null , undefined , '' (empty string)m and false .

So those expressions will all be true if objec[propertyName] is any of those values.

Conclusion

We can check for arrays with Array.isArray .

And we shouldn’t check for all falsy values to check if an object property exists.

We can detect functions with the typeof operator.

Categories
JavaScript Best Practices

Maintainable JavaScript — Function Invocation and Equality

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at variables and functions.

Function Invocation

We should wrap our immediate function invocations with parentheses so that we can tell them apart from function declarations.

For instance, we should write:

const doSomething = (function() {
  //...
  return {
    message: "hello"
  }
})();

We surround the functions with parentheses so that we know we’re calling it.

Strict Mode

ES5 introduced strict mode, which changes how JavaScript is executed and parsed to reduce errors.

To enable strict mode, we add 'use strict' above the code we want to enable strict mode on.

It’s a common recommendation to avoid placing 'use strict' in the global scope.

This is because if we’re concatenating multiple files into one, then we enable strict mode in all of them.

There’s a good chance that we’ll have errors in our old code if we have strict mode on all the code.

So before we fix all the code to follow strict mode, we should enable strict mode partially.

For instance, instead of writing:

"use strict";

function doSomething() {
  // ...
}

We should write:

function doSomething() {
  "use strict";
  // ...
}

If we want to enable strict mode on multiple functions, we should write:

(function() {
  "use strict";

  function doSomething() {
    // ...
  }

  function doMoreWork() {
    // ...
  }
})();

This is good since we keep strict mode within the function.

New code should always have strict mode on.

It corrects many mistakes like accidentally assigning to built-in global variables and other things.

Modules always have strict mode by default, so we always have to follow it.

Arrow Functions

Arrow functions are a newer kind of function introduced with ES6.

It’s great for defining functions that aren’t constructors since they don’t bind to their own this and doesn’t have its own instance methods.

Another benefit is that it doesn’t bind to the arguments object so that we can’t use it to get the arguments of the function call.

For instance, we can write:

const doSomething = () => {
  // ...
}

to define an arrow function.

Since they don’t bind to their own this value, they’re great for callbacks.

Equality

Equality with == or != is tricky because of type coercion.

Type coercion causes variables of a specific type to be converted automatically for an operation to succeed.

This can lead to some unexpected results.

== and != will do type coercion to its operands when we use them.

If the values don’t have the same data type, then type coercion will be done to one or both operands.

There’re many cases where they don’t do what we expect.

For example, we have:

console.log(5 == "5");

and logs true .

And:

console.log(25 == "0x19");

also returns true .

This is because type coercion is done with the Number function.

The strings are converted to numbers before doing the comparison.

This is one reason to avoid using == and != for comparisons.

Instead, we use === and !== .

Conclusion

We should be careful with function invocations.

Also, arrow functions are great for callbacks.

And comparisons should be done with === and !== .

Categories
JavaScript Best Practices

Maintainable JavaScript — Declarations and Function Calls

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at variables and functions.

Variable Declarations Hoisting

The var statements are hoisted.

This means the declaration can be accessed before it’s defined.

This is because the declaration is pulled up to the top without the value.

We don’t want that to happen automatically since it’ll confuse us.

Fortunately, there’s the let and const variables which are block-scoped and aren’t hoisted.

This is great since we won’t be tricked by where a variable is available in the code.

To make our lives even easier, we should put the variables as the first statements in a function.

A function shouldn’t be too long so we won’t have to jump too far.

For instance, we can write:

function doWork(items) {
  let i;
  const len = items.length;
  let value = 10;
  let result = value + 10;
  for (i = 0; i < len; i++) {
    console.log(items[i]);
  }
}

We used let and const everywhere.

The items are iterated through as with the for loop.

len is declared with const since it never changes.

Doug Crockford also recommended this style.

We also combine all variable declarations with one value per line.

Function Declarations

Function declarations are also hoisted by JavaScript engines like variable declarations.

So we can use a function declaration in our code before it’s declared.

For instance, we can write:

doSomething();

function doSomething() {
  console.log("hello world");
}

We called the doSomething before the function is declared.

This works because of the hoisting.

However, to reduce confusion, it’s better to declare our functions before they’re used.

So we should write:

function doSomething() {
  console.log("hello world");
}

doSomething();

instead.

ESLint and JSHint will warn when a function is used before it’s declared.

Function declarations should never appear inside block statements.

For instance, we shouldn’t write code like:

if (condition) {
  function doSomething() {
    console.log("foo");
  }
} else {
  function doSomething() {
    console.log('bar')
  }
}

This isn’t valid syntax but it’s still run by some browsers.

Therefore, it’s a gray area in the ES specification that should be avoided.

Function declarations should only be used outside of conditional statements.

This is also forbidden in the Google style guide.

Function Call Spacing

Function call spacing is something that isn’t too controversial.

Most people agree that function calls should have no spaces between the function name and the opening parentheses.

This is done to differentiate it from block statements.

For instance, we should write:

doWork(item);

instead of something like:

doWork (item);

This is to differentiate it from block statements like:

while (item) {
  // ...
}

Doug Crockford’s code conventions call this out.

Google’s style guide also recommends this style.

Immediate Function Invocation

JavaScript lets us declare anonymous functions without property names and assign those functions to variables and properties.

We can write:

const doSomething = function() {
  // ...
};

We assigned the anonymous function to a variable, so we can call it with the variable name.

It’s bad to call it without parentheses surrounding the function itself.

For instance, we shouldn’t write:

const value = function() {
  //...
  return {
    message: "hello"
  }
}();

It’s easy to confuse that with defining the function and assigning it to a variable without calling it.

Conclusion

var and function declarations are hoisted.

So they shouldn’t be used.

Also, we shouldn’t use immediate function invocations without parentheses.