5 JavaScript Pitfalls To Avoid

Content:

JavaScript is a language that’s both accessible, and rewarding to learn.

It’s not without its quirks, though, some of which can easily catch out even the most senior JS developer.

Here’s a list of 5 JS pitfalls to avoid.

1. Dynamic Variable Types

JS is a loosely-typed language. This means you don’t set the data type when creating a variable – JS dynamically works this out for you.

When comparing data of different types (using ==), JS will use type coercion to ensure variables are the same type before comparing them.

This is nothing unique – many languages act in a similar manner.

However, because variable types are dynamic, there’s potential for a variable type to be different from the one you’re expecting.

Some of the effects of type coercion will show up in later points, as it contributes to some of the other quirks of the language.

2. Concatenation vs Addition

This one is really a follow on from the previous point, and a great example of an issue dynamic typing can cause.

The symbol for concatenation in JS is ‘+’, the same symbol used for addition. As a result, you need to consider the data types you’re using alongside +.

Generally speaking, where a string is involved, JS will concatenate.

"3" + 1 = 31

In this example, the 1 is coerced to a string, then concatenated to “3”, to form the string “31”. You can verify this is a string using typeof.

x = "3" + 1
typeof(x)  // -> "string"

Where each value is a number, they will be added.

3 + 1 = 4

If a string is present, all ‘+’ operations after the string will be treated as concatenations.

3 + 1 + "4" = 442

For this reason, it’s a good idea to split this type of statement up to keep the types separate.

x = 3 + 1  // -> 4
x += "4"   // -> 44

When using variables, it’s important to keep on top of your expected data types, to ensure your + is doing what you expect.

If you can’t be certain of the data you’ll be getting, it’s best to cast it to the type you want beforehand.

x = 3
y = 4
z = x.toString() + y.toString() // -> 34

3. Variable Scope

In most programming languages, a variable is block scope-limited, and is only accessible within the block it’s declared in. JS, however, is slightly different.

for (var i=0; i<10; i++) {
    // do things
}

console.log(i);

You’d expect that the console.log call would throw an error, or print undefined, as i should only be in scope inside the for loop.

What you’ll actually, find, is the result

10

The reason for this is the default scope used by the var keyword. Variables initialised using var use function scope, meaning they are accessible anywhere in the function in which they’re declared. This is true even before a value has been set, due to variable hoisting. Learn more about hoisting in our guide here.

This can be avoided by using let, rather than var. let was introduced in ES6.

When using let, the code behaves slightly differently. let uses block scoping, which is more similar to variable handling in other languages.

for (let i=0; i<10; i++) {
    // do things
}

console.log(i);

We’ll now get the result we were expecting.

Uncaught ReferenceError: i is not defined

4. Sorting Integer Arrays

So, you have an array full of numbers. You want to sort them. JavaScript has a function (Array.prototype.sort()) to do this. Excellent. Right?

[14, 7, 1, 5].sort()  // -> [5, 14, 1, 7]

Wait, what?

Unfortunately, Array.prototype.sort() doesn’t quite work how you might expect.

Even when faced with an array full of integers, it sorts them based on their string representation. So what we’re actually doing is

[14, 7, 1, 5].sort()
[fourteen, seven, one, five].sort()
[five, fourteen, one, seven]
[5, 14, 1, 7]

One way around this is to pass the following function to the sort function.

(a, b) => a - b
[14, 7, 1, 5].sort((a, b) => a - b)

This works by comparing each value with the next value in the array. If the second value is larger, a negative number is returned. If the first is larger, the result is positive. If they’re the same, the result is 0.

The .sort() function doesn’t care about the magnitude of the result – the returned value will always be either -1, 0 or 1.

If the result is 1, the first value is sorted after the second, as it is larger.

A potentially faster way of doing this is using the TypedArray feature introduced in ES6.

let arraySort = new Int32Array([14, 7, 1, 5]);
arraySort.sort();  // -> [1, 5, 7, 14]

If you already have a standard array containing your values, you’ll need to convert it to a TypedArray first.

let arraySort = Int32Array.from(array);

And if you want to use the result as a standard array, you’ll need to convert it to one.

arraySort = [...arraySort]

This method is a little more cumbersome, so is only worth exploring for very large data sets.

5. Cumbersome DOM Manipulation

Ok, so this one won’t actually break anything, which is why it’s been left until the end. But it’s something that a lot of JS devs do, without realising the impact it has on performance.

JS is frequently used to manipulate the DOM, to create dynamic pages. You’ll see a lot of code such as

const container = document.querySelector('container');

const textBox = document.createElement('div');
container.appendChild(textBox);

const text = document.createElement('p');
textBox.appendChild(text);

const image = document.createElement('img');
container.appendChild(image);

Manipulating the DOM is a costly process, so on a page with multiple calls, the performance hit can start to become noticeable.

You can avoid this by creating a document fragment instead.

const container = document.querySelector('container');
const fragment = document.createDocumentFragment();

const textBox = document.createElement('div');
const text = document.createElement('p');
const image = document.createElement('img');

textBox.appendChild(text);
fragment.appendChild(textBox);
fragment.appendChild(image);

container.appendChild(fragment.cloneNode(true));

By adding the created elements to the fragment, we’re building the section we want to add outside of the DOM. Only once the entire thing is ready will the fragment be added, only requiring the DOM to be manipulated once.

If you like what we do, consider supporting us on Ko-fi