Top Common Mistakes in JavaScript

BitBute
6 min readAug 19, 2021

Working with industry veterans and beginners, I decided to share some of the most common mistakes made in JavaScript.

Using Global Variables

The road to programming hell is paved with global variables,
― Steve McConnell,
Code Complete

Using global variables is considered bad not only in JavaScript but any programming language.

Whenever we use a variable, we are in charge of its complete life-cycle. This includes the time at which it is initialized when its values are changed or updated and lastly when it is destroyed. This is usually referred to as the scope of the variable.

Using local variables makes it really easy since we delegate initialization and destruction to the garbage collector of the language. Also, the change is within the function’s scope, making it easier for us to keep track of it.

Unfortunately, this is not the case with global variables. It becomes our responsibility to control the usage and value changes. Since the variable’s scope is practically the whole application, it becomes nearly impossible to predict its value.

This benign-looking code has a bug lurking underneath. Even w

This benign-looking code has a bug lurking underneath. Even worse is that if it is tested only once, we will see expected results.

The issue actually occurs when we do multiple executions. At this point, you may have realized the problem.

The errors variable is only initialized once and never reset. Hence between each processItems calls, the results are unpredictable. One way to solve this would be to clear the errors array at the beginning of the processItems function.

However, this only patches the problem. It doesn’t really solve it. The real solution is to eliminate the root cause. i.e. get rid of the global variable.

As you can see we are passing errors around. It may seem “unnecessary” compared to the previous code. But, if you want to avoid unpredictable results, this is the right approach.

Conclusion

Avoid global variables as a rule of thumb. Even if it means passing additional variables around.

Strict Equality

Beginners tend to use == operator freely without understanding that JavaScript has 2 types of equality Operators.

This is nicely covered in this StackOverFlow Answer

Which-equals-operator-vs-should-be-used-in-javascript-comparisons

JavaScript has two sets of equality operators: === and !==, and their evil twins == and !=. The good ones work the way you would expect. If the two operands are of the same type and have the same value, then === produces true and !== produces false. The evil twins do the right thing when the operands are of the same type, but if they are of different types, they attempt to coerce the values. the rules by which they do that are complicated and unmemorable. These are some of the interesting cases:

'' == '0'           // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
' \t\r\n ' == 0 // true

Conclusion

Always use === instead of == to avoid unpredictable behaviors.

With the addition of map, forEach, filter, developers have started using these new concepts. In these functions, one has to pass a callback function which is treated as sequential.

Let’s check an example. Suppose we want to create a function that processes all items of an until it receives a break value at which it should stop processing.

for-loop vs forEach

As we can see the outputs are different. This is because forEach accepts a callback function as opposed to for-loop which is just running the code sequentially. The return keyword in the callback function is only applicable to that inner function, not to processItems2.

This raises another question.

Is it possible to achieve the same with forEach?

There can be many ways to do this. In general, we will have to sanitize the array beforehand.

Misreading sequential vs parallel promises

Second confusion that occurs often among new developers when using callbacks.

Suppose processItem is an async function. Your job is to process items one at a time. This is easily done using traditional for-loops.

Do you think the example of forEach executing them sequentially?

It is certainly not. What is happening is that we are creating promises by using async/await. But we do not wait for one to complete before moving to the next item. This is because forEach takes a callback function and returns something. In this case, it is returning a Promise, not the value of the Promise.

Can we use forEach for this use-case somehow?

We can. If you need to handle promises sequentially, you need to bind them one after the other. It is possible only by using a thenable or using the 1st approach i.e for-loop.

Even though the above code gave us sequential execution of promises, I do not recommend this approach since it makes code less readable and readability should be our top concern always. That is why I recommend using for-loop.

However, if you are looking for parallel execution, then we have a nifty API Promise.all which helps us greatly along with map.

MSDN Promise

Moreover Promise.all returns us the result of all the promises in an array for further processing.

Conclusion

  1. for-loop and forEach behave differently. Do not confuse them.
  2. When working with Promise, be clear if you need to process them sequentially or parallel. Accordingly, make the right choice.
  3. Using await keyword won’t make the execution sequential if it is inside a callback function.

Mixing map, forEach, and filter

A lot of developers get confused when to use what and use these methods incorrectly. Below are some of the incorrect examples.

Let’s see what’s wrong with each of them.

  1. As already discussed above, return keywords inside callback functions are for the callback function, not the enclosing one.
  2. We are using map , but the callback function is not returning any value. This seems to require a forEach not map.
  3. Only some of the callbacks will return value based on if condition. Others will return undefined. Hence the nonPrintable array will have undefined and item mixed.

When to use map and when to use forEach?

map is a 1:1 relationship function. For every item you give, you want another modified item.

E.g. Suppose you want to double all the numbers. i.e. Given [1,2,3], you want [1, 4, 6]. This is a map. const doubles = [1,2,3].map(i => i * 2).

If you don’t care about return values or you will return only on some condition, then it is not a map, it is a forEach.

When to use filter?

When you want a subset of values based on certain conditions, then you need a filter.

Let’s fix the above 3 examples now.

Conclusion

  1. If you input array and output array has a 1:1 relationship, use map.
  2. If you need to process only a subset of items, use filter.
  3. If you don’t care about returning output per iteration, use forEach.
  4. If you want to process async items sequentially, use forEach.
  5. If you want to process async items in parallel, use map.

MSDN Array

MSDN Promise

Using Object.assign

Many times, I have noticed developers using Object.assign incorrectly when merging 2 objects.

Consider the example below. Our task is to add a new property upgraded: true to our 2 inputs.

Why did the second variable get a: hello?

This is because, Object.assign was used incorrectly. This function copies all of the sources into the target. So, after 1st usage, the object upgrades changed. It got the properties of input1 and then we assigned all of them to upgrades again on next line along with input2.

What is the correct usage?

Using empty new target objects

Conclusion

  1. If you don’t want to change input variables, choose empty object targets in Object.assign.
  2. Another option is to use spread operators which copy the properties and returns a new object.
    const upgraded = {...upgrades, ...input1}.

Further readings

  1. MSDN Object.assign
  2. MSDN Spread Operator

Recommended Book
JavaScript: The Good Parts

Source
https://bitbute.tech/2021/08/12/top-common-mistakes-in-javascript/

--

--

BitBute

Founder at BitBute.tech. Pushing the technology boundaries in Web and Mobile Development. Blogging to share knowledge