Categories
JavaScript Best Practices

Look for Code Smells in JavaScript

In programming, a code smell is a characteristic of a piece of code that indicates there may be deeper problems. It’s a subjective characteristic used for judgment of whether the code is decent quality or not by looking at it.

In JavaScript, code smells still apply. Since there’re old constructs that should be eliminated, there’re additional issues that may arise from looking at old code.

In this article, we’ll look at some code smells using JavaScript code as examples. The code smells we look at include duplicate code, complex code, shotgun surgery, and classes that are too large.


Common Code Smells

Duplicate code

Duplicate code is code that is repeated in different places doing the same thing. Pieces of code that are very similar may also be considered duplicate.

This is a problem because we have to change multiple pieces of code when we have to change duplicate code. Also, it’s easy to forget that they’re there by other developers.

This is why the don’t repeat yourself (DRY) principle is emphasized a lot. It saves developers time.

For example, if we have

let numApples = 1;

in a code file, then we use that everywhere if we want to get the number of apples.

If we have repeated code that’s run many times, then we use a loop. If we want to run a snippet of code in multiple places but not repeated, then we can write a function and run that.

For instance, if we have something like:

let str = '';
str += 'foo';
str += 'bar';
str += 'baz';

We should instead write:

const arr = ['foo', 'bar', 'baz'];
let str = '';
for (let word of arr) {
  str += word;
}

If we want code to be available in multiple modules, we export it and import it elsewhere.

For example, we can write the following in a JavaScript module called module.js and import the foo function elsewhere:

export foo = () => 'foo';

and call it as follows:

import { foo } from 'module';
const bar = foo();

Code That’s Too Complex

If there’s a simpler solution, we should go for that instead of writing something more complex.

Simple solutions are easy to read and change.

For example, if we have:

let str = '';
str += 'foo';
str += 'bar';
str += 'baz';

to create the string 'foobarbaz' , we can simplify it to:

let str = ['foo', 'bar', 'baz'].join('');

Shotgun Surgery

Shotgun surgery is a change that requires code to be multiple pieces of code to be changed.

This is a problem because changes are required in multiple places, which makes making the change difficult. It’s also easy to miss multiple places.

It’s usually caused by adding code that’s run in multiple places in the wrong place. There’s usually a better way to change code than to write code that’s used in multiple places.

For example, if we have an Express app and we want to add logging to it, we shouldn’t do the following:

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/foo', (req, res) => {
  console.log('foo called');
  res.json({ message: 'foo' });
});

app.get('/bar', (req, res) => {
  console.log('bar called');
  res.json({ message: 'bar' });
});

app.get('/baz', (req, res) => {
  console.log('baz called');
  res.json({ message: 'baz' });
});

app.listen(3000, () => console.log('server started'));

The code above isn’t good because we have to add console.log to every route.

Instead, we should refactor the console.log into a route middleware that’s run before the code route code runs:

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.use((req, res, next) => {
  if (req.path.substring(1)) {
    console.log(`${req.path.substring(1)} called`);
  }
  next();
});

app.get('/foo', (req, res) => res.json({ message: 'foo' }));

app.get('/bar', (req, res) => res.json({ message: 'bar' }));

app.get('/baz', (req, res) => res.json({ message: 'baz' }));

app.listen(3000, () => console.log('server started'));

As we can see, the code above is much cleaner and it achieves the same thing as before, except that we only have one console.log instead of one for each route.

If we have more routes, then it’s going to be even harder to change the logging code. For example, if we want to replace console.log with some logging library, then it’s going be way harder if we put the logging code into every route.


Large Classes

Any class that’s large is going to be hard to maintain. Classes are supposed to create objects that only do one thing. We don’t want classes that are so big that they’re doing many things.

An object that’s too large is called a god object. A god object is an object that does too much. It’s very big and it does many things.

Each class is only supposed to solve small problems. If they’re large, then they’re probably doing too much.

Another issue that may arise from having classes that are too large is that different parts of the class depend on each other. The tight coupling creates lots of headaches when sorting out the code.

To solve this, we should the big class into smaller classes that does different things. Common functionality between them can be refactored into a parent class.

For example, if we have the following Employee class:

class Employee {
  eat() {
    //...
  }
  drink() {
    //...
  }
  work() {
    //...
  }
  sleep() {
    ///...
  }
  walk() {
    //...
  }
}

this is an example of a class that’s doing to much because it has methods that aren’t directly relevant for employees.

Instead, we can break the class into two classes as follows:

class Person {
  eat() {
    //...
  }
  drink() {
    //...
  }
  sleep() {
    ///...
  }
  walk() {
    //...
  }
}

class Employee extends Person {
  work() {
    //...
  }
}

Now the Employee only has the method that’s relevant to employees, and that’s the work method. We move the methods that are common to all people to the Person class so we can reuse them and declutter the Employee class.

It’s easy to clean up code and make it easy to read and change by following some basic principles. First, move duplicate code to a common location and reuse the code from the common location. Second, don’t create classes that do too many things. Third, if we need to make a code change in many places at once, there’s probably a better way to do it. Finally, don’t make code that’s too complex.

Categories
JavaScript Best Practices

JavaScript Best Practices — DOM Performance

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing JavaScript code.

Too Many Interactions with the Host

We shouldn’t interact too much with any DOM objects.

They render slowly and we should reduce the number of interactions that are done.

Too Many Dependencies

If we have too many dependencies, then we should reduce them.

Over the years, many things are added to the JavaScript standard library.

So we don’t have to use 3rd party dependencies to do the same thing.

For instance, Lodash array methods can be replaced with built-in array methods.

3rd party HTTP clients can be replaced with the Fetch API.

Poor Event Handling

To improve performance of event handling, we should use them properly.

We should remove unnecessary loops and unbind unused event handlers with jQuery unbind or plain JavaScript’s removeEventListener to remove them.

Unorganized Code

To make our lives easier, we should organize our code into coherent chunks.

This way, we can find the code later.

Use HTTP/2

HTTP/2 is the latest version of JavaScript and provides many enhancements over the original version.

We’ll see speed improvements if we switch to HTTP/2 server for hosting our site.

Use Pointer References

We should catch our DOM objects in a variable so that we don’t have to get them again from the DOM if we need it later.

We just take the variable and use that.

For instance, we can write:

const fooEl = document.querySelector('.foo');

to get the element with the class foo .

Trim our HTML

We can reduce the number of items that needed to be loaded by reducing the elements on our page.

The fewer we have, the less we need to load, and the faster our page will be.

Use document.getElementById()

We can use getElementById .

Getting things by ID is faster since there’s only one element with a given ID on a page.

This means our browser doesn’t have to check all the nodes to find all the items.

For example, we can write:

const button = document.getElementById('window-minimize-button');

Use document.querySelector()

If we only want to get one element with the given selector, we can use querySelector to get the DOM node with the given selector.

The browser stops looking after the first node with the given selector is found, which makes the lookup faster.

For example, we can write:

`const button = document.`querySelector`('#window-minimize-button');`

Batch our DOM Changes

We should batch our DOM changes so that we won’t rerender multiple times.

This way, our web page would feel faster for the user.

Buffer our DOM

If we have scrollable divs, we can use a buffer to remove items from the DOM that aren’t currently visible in the viewport.

This will save memory usage and DOM traversal.

There are many libraries to help us with this like React Virtualized for React apps.

Conclusion

We can apply various techniques to speed up DOM manipulation.

These can make a big difference in performance.

Categories
JavaScript Best Practices

JavaScript Best Practices — Readability

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing JavaScript code.

Don’t Use Logical Operators as Statements

We shouldn’t write clever but hard to read code.

For example, something like:

a() || b.foo.bar(); m && c() ? d() : l || m.baz[bee](3);

is impossible to read.

And therefore this should be avoided.

Always Declaring Variables at the Top of Their Scope

We can put variables at the top of their scope:

function calculate() {
  let width, height, length;
  // ...
}

This way, they’re easy to see and we can work with them anywhere.

Repeatedly Used Inline Object Literals

If we have object literals that are repeatedly used with some variation, we can put them in a function.

For example, we can write:

function prepareRequest(mode, updateId, value) {
  const req = {
    mode,
    updateId,
    value,
    uid: genUID()
  };
  //...
}

This way, we can call prepareRequest and use it anywhere.

Complex Inline Regex

Complex inline regex is impossible to read.

For example, if we have:

if (/^((\[^<>()[]\.,;:\s@"\]+(\.\[^<>()[]\.,;:\s@"\]+)\*)|(".+"))@(([\[0-9\]{1,3}\.\[0-9\]{1,3}\.\[0-9\]{1,3}\.\[0-9\]{1,3}\])|((\[a-zA-Z\-0-9\]+\.)+\[a-zA-Z\]{2,}))$/.test(str)) {
  // ...
}

We have no idea what it’s checking.

Instead, we can assign it to variable and then use it like:

const emailRegex = /^((\[^<>()[]\.,;:\s@"\]+(\.\[^<>()[]\.,;:\s@"\]+)\*)|(".+"))@(([\[0-9\]{1,3}\.\[0-9\]{1,3}\.\[0-9\]{1,3}\.\[0-9\]{1,3}\])|((\[a-zA-Z\-0-9\]+\.)+\[a-zA-Z\]{2,}))$/

if (emailRegex.test(str)) {
  // ...
}

Now we know the regex is actually used for checking for a valid email address.

Strict Equality Everywhere

Strict equality just helps us with avoiding issues with automatic data type coercion.

The rules for data type conversion is complex with the == or != operators.

So we should instead use the === and !== operators to check for equality and inequality respectively.

For example, we can write:

if (typeof x === 'string' && x === 'abc') {
  // ...
}

to compare their type and value.

Assuming Truthiness Equals Presence

We shouldn’t assume truthiness means something ins present in an object.

To check if a key is in an object, we should use hasOwnProperty or the in operator.

Instead of writing:

if (!cache[key]) {
  cache[key] = value;
}

We should write:

if (!cache.hasOwnProperty(key)) {
  cache[key] = value;
}

or:

if (!(key in cache)) {
  cache[key] = value;
}

The in operator also checks the prototype for a property.

Commenting Nothing or Commenting Every Little Thing

No comments and commenting on everything are both bad.

Sometimes there are things that need an explanation that isn’t in the code.

However, it’s also bad to make comments on things that are already covered by the code.

Write Something Reliable without Unit Tests

It’s just hard to write something reliable without unit tests.

They definitely give us peace of mind when we’re changing code.

Using Long Names and Being Overly Specific

We shouldn’t have names that are too long and are overly specific.

Names like:

function generateNewtIDForANewRequestCacheItem() {...}
function deleteRequestCacheItemsThatDoNotEqual(value) {...}

They are definitely too long and too specific.

We should definitely find ways to reduce the length of them.

They have lots of extra information we don’t need.

Conclusion

We should take note of some practices to make our code easier to read.

We may go overboard with some good practices as well.

Categories
JavaScript Best Practices

JavaScript Best Practices — Arrays and Exceptions

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing JavaScript code.

Avoid Using for-in Loop for Arrays

We should avoid using for-in loop for arrays.

For instance, instead of writing:

let sum = 0;
for (let i in arrayNumbers) {
  sum += arrayNumbers[i];
}

We can use a for loop or a for-of loop:

let sum = 0;
for (let a of arrayNumbers) {
  sum += a;
}

or:

let sum = 0;
for (let i = 0, len = arrayNumbers.length; i < len; i++) {
  sum += arrayNumbers[i];
}

A for-in loop is used for iterating through regular objects rather than arrays.

Pass Functions, not Strings, to setTimeout() and setInterval()

We should always pass functions to setTimeout and setInterval .

For example, instead of writing:

setInterval('doSomething()', 1000);
setTimeout('doSomething()', 5000);

We write:

setInterval(doSomething, 1000);
setTimeout(doSomething, 5000);

Use a switch/case Statement Instead of a Series of if/else

switch and case are shorter than a series of if and else .

For example, we can write;

switch (val) {
  case 1:
    //...
    break;
  case 2:
    //...
    break;
  case 3:
    //...
    break;
  case 4:
    //...
    break;
  default:
    //...
}

This is better organized than:

if (val === 1) {
  //...
}
else if (val === 2) {
  //...
}
else if (val === 3) {
  //...
}
else if (val === 4) {
  //...
}
else {
  //...
}

If we have more than 10 cases, we should avoid a series of if and else s.

Use switch/case Statement with Numeric Ranges

We can use switch and case with numeric ranges.

To do that, we can write:

switch (true) {
  case isNaN(age):
    category = "not an age";
    break;
  case (age >= 50):
    category = "old";
    break;
  case (age <= 20):
    category = "kid";
    break;
  default:
    category = "young";
    break;
};

Instead of passing a variable switch , we pass in true .

Also, the case has expressions instead of a value.

Create an Object Whose Prototype is a Given Object

We can create an object whose prototype is the gin object with he Object.create method.

For example, we can write:

const obj = Object.create(proto);

where proto is the prototype object for obj .

An HTML Escaper Function

We can escape HTML with some regex.

For example, we can write:

function escapeHTML(text) {
  const replacements = {
    "<": "&lt;",
    ">": "&gt;",
    "&": "&amp;",
    """: "&quot"
  };
  return text.replace(/[<>&"]/g, (character) => {
    return replacements[character];
  });
}

We search for the brackets, ampersands, and quotes with the regex and then we get the replacement character from the replacements object.

Avoid Using try-catch-finally Inside a Loop

try-catch-finally creates a new variable in the current scope at runtime each time the catch clause is run.

So instead of writing:

let arr = ['foo', 'bar'], i;
for (i = 0, len = arr.length; i < len; i++) {
  try {
    // ...
  } catch (e) {
    // ...
  }
}

We write:

let arr = ['foo', 'bar'],  i;
try {
  for (i = 0, len = arr.length; i < len; i++) {
    // ...
  }
} catch (e) {
  // ...
}

Now the catch clause can only be run once with the 2nd example.

So variables in there can only be created once.

Conclusion

We shouldn’t use for-in loops with arrays.

Also, we can create objects with a prototype with Object.create .

There’re also many things to watch out for with arrays.

Categories
JavaScript Best Practices

Robust JavaScript Best Practices

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing JavaScript code.

Consider Using try/catch When Using JSON.parse

There’s always a chance that we encounter invalid JSON strings.

So to prevent that from crashing our app when we parse JSON, we should wrap try/catch around our JSON.parse call.

For example, we can write:

try {
  JSON.parse(str);
}
catch(ex) {
  console.error(ex);
}

This way, we won’t crash our app when we try to parse invalid JSON.

Using Classes

Classes are just syntactic sugar above prototypes.

However, it looks more familiar to most people.

And it’s easier to understand than prototypes.

For example, instead of writing:

function Dog(name) {
  this.name = name;
}

Dog.prototype.speak = function() {
  console.log(this.name);
}

We write:

class Dog {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(this.name);
  }
}

They do the same thing, but the 2nd example makes more sense.

We can definitely consider them when we create constructors.

Using Arrow Functions

Arrow functions are useful because they don’t bind to their own this and it’s shorter.

This means it’s handy for arrow functions.

If we don’t need its own this inside the function, we should use arrow functions.

For instance, we can write:

const foo = (msg) => {
  console.log(msg);
};

or:

const init = (msg) => console.log(msg);

to write them.

This makes dealing with JavaScript functions much easier.

Concatenating Strings and Templating

Tenplate strings are great alternatives to concatenation.

We can interpolate expressions in a string without having complex concatenation expressions.

For instance, instead of writing:

const msg = 'I said, "hello' + firstName + ' ' + lastName + '" to the world.';

We can write:

const msg = `I said, "hello ${firstName} ${lastName}" to the world.`;

It’s much cleaner and it’s also a bit shorter.

Destructuring Arrays and Objects

The destructuring syntax it’s another great JavaSctipt feature.

Before the destructuring syntax existed, destructuring array entries to variables is a lot harder.

We’ve to write:

const arr = [1, 2, 3, 4];
const a = arr[0];
const b = arr[1];
const c = arr[2];
const d = arr[3];

to assign each entry to its own variable.

With the destructuring syntax, it’s much shorter:

const [a, b, c, d] = [1, 2, 3, 4];

With objects, instead of writing:

const obj = { foo: 1, bar: 2, baz: 3 };
const foo = obj.foo;
const bar = obj.bar;
const baz = obj.baz;

We write:

const { foo, bar, baz } = { foo: 1, bar: 2, baz: 3 };

It’s much simpler.

Componentizing Our Code

We can organize our code into modules.

To do that, we just create JavaSctipt files that has export statements.

For example, we can write:

bar.js

export const foo = 66;

Then we can import it by writing:

import { foo } from 'bar';

It’s much easier than using scripts with a bunch of global variables, which can easily be overwritten.

It’s a basic building block of JavaScript apps.

Conclusion

We can use modern JavaScipt constructs like the class syntax, modules, and destructuring to make our lives easier.