Categories
JavaScript Mistakes

More JavaScript Mistakes — Objects, Async Code and Event Listeners

Spread the love

JavaScript is a language that’s friendlier than many other programming languages in the world. However, it’s still very easy to make mistakes when writing JavaScript code through misunderstanding or overlooking stuff that we already know. By avoiding some of the mistakes below, we can make our lives easier by preventing bugs and typos in our code that bog us down with unexpected results. In this article, we’ll look mistaking object keys as array indexes, not understanding asynchronous code, misusing event listeners.

Confusing Object Keys and Array Indexes

In JavaScript, the brackets can be used to accept an object which can take a string containing a property name and then get the value from it. For example, if we have the following object:

const obj = {
  foo: 1,
  bar: 2
}

Then we can access the value of the foo property by writing either:

obj.foo

or we can use the bracket notation and write:

obj['foo']

The bracket notation is also used to access an array entry by its index. For example, if we have:

const arr = [1, 2, 3];

Then we write:

arr[0]

to get the first entry of the arr array.

We have to be careful in that we don’t confuse normal objects and arrays since the values of them can be accessed with the bracket notation, and the JavaScript interpreter doesn’t distinguish between the 2. This means that code like:

arr['foo']

are still allowed in JavaScript. It doesn’t stop us from commit the mistake of accessing an array-like we do with a regular object. If we log the value of arr[‘foo’] we just get undefined .

If we don’t know whether a variable is a regular object, an array or something, else, then we can use the Array.isArray method which checks if a value that we pass in if an array. It takes one argument which is any objects that we want to check to see whether if it’s an array.

By using the Array.isArray method, we’ll be sure that we’re accessing an array if Array.isArray returns true. For example, we can use it as the following code:

const arr = [1, 2, 3]

if (Array.isArray(arr)) {
  console.log(arr[0]);
}

This way, we’re sure that arr is an array before trying to access an array entry.

Not Understanding Asynchronous Code

Because of the single thread nature of JavaScript, lots of code is going to be asynchronous since running code line by line all the time is going to hang the computer if it’s waiting for something that takes some time such as a response from an HTTP request.

With asynchronous code, we often have run code that’s in a callback function, which runs in an indeterminate amount of time. They can’t be treated like synchronous code that runs line by line.

For example, if we want to get some data from a GET request, we often have to use an HTTP client that makes the request asynchronously and the response is obtained in an indeterminate amount of time.

If we use the Fetch API built into most modern browsers, we’ll get the response data asynchronously. For example, we’ll have code that looks something like the following:

fetch('[https://jsonplaceholder.typicode.com/todos/1'](https://jsonplaceholder.typicode.com/todos/1%27))
  .then((response) => {
    return response.json()
  })
  .then((responseBody) => {
    console.log(responseBody);
  })

As we can see we have multiple callback functions that do various things. If we write something like:

const response = fetch('[https://jsonplaceholder.typicode.com/todos/1'](https://jsonplaceholder.typicode.com/todos/1%27))

const responseBody = response.json()
console.log(responseBody);

It’s not going to work since the correct code doesn’t run code line by line. In the correct code, the fetch function is called, which returns a promise, then inside the callback function in the first then method, we return the result of theresponse.json() object, which returns another promise which resolves to the JSON body of the response. Then in the callback function of the second then method, we log the responseBody which is obtained by resolving the promise returned by response.json() call.

Promises may be run sequentially by chaining them, but they don’t run instantaneously and so they don’t run like synchronous code.

A shorthand that looks more like synchonous code would be using the async and await keyword introduced with ES2017. We can write:

fetch('[https://jsonplaceholder.typicode.com/todos/1'](https://jsonplaceholder.typicode.com/todos/1%27))
  .then((response) => {
    return response.json()
  })
  .then((responseBody) => {
    console.log(responseBody);
  })

to:

(async () => {
   const response = await fetch('[https://jsonplaceholder.typicode.com/todos/1'](https://jsonplaceholder.typicode.com/todos/1%27));
   const responseBody = await response.json();
   console.log(responseBody);
 })();

An async function looks like it’s running line by line, but it’s just a shorthand for the then method calls with callbacks passed inside. We can’t return anything other than a promise inside an async function.

Adding Too Many Event Listeners

If we have the following HTML code:

<input type="checkbox" />

<button>Click</button>

and the corresponding JavaScript code:

const checkbox = document.querySelector('input[type=checkbox]');
const button = document.querySelector('button');

checkbox.addEventListener('change', () => {
  if (checkbox.checked) {
    button.addEventListener('click', () => {
      alert('Alert');
    });
  }
});

Then we’ll see that if we toggle the checkbox on and off a few times and then click the button, we’ll see the ‘Alert’ pop up multiple times. This is because we used the addEventListener method on the button object, which represents the button element that we have in the HTML. The addEventListener method attaches a new click event listener each time the checkbox is checked. This means that we have multiple event listeners listening to the click event of the button. When the click event is finally fired by clicking the button, all the click event listener functions that we attached to the button all run, causing the ‘Alert’ pop up to appear multiple times.

This means we attached too many event listeners to the button.

The right way to do this is to set the onclick property of the button with the same event listener function instead. Likewise, we can also do the same thing for the checkbox object even though we don’t have the same problem for consistency. We can instead write:

const checkbox = document.querySelector('input[type=checkbox]');
const button = document.querySelector('button');

checkbox.onchange = () => {
  if (checkbox.checked) {
    button.onclick = () => {
      alert('Alert');
    };
  }
};

This way, we keep overwriting the onclick event listener assigned to the button.onclick property instead of keep attach new click event listeners to it.

In JavaScript, the brackets can be used to accept an object which can take a string containing a property name and then get the value from it. With arrays, we can use the bracket notation to access an array element by its index. There’s no way to distinguish whether an object with an array just by looking at the code. Therefore, we should check whether an object is actually an array before trying to access items by its index.

Lots of code in JavaScript are asynchronous since JavaScript is single-threaded, so having all the code run line by line will stall the browser. This means that we have to use asynchronous code to avoid stalling the computer by letting code that doesn’t return a result immediately wait in the background. We have to be aware that code are in callback functions of asynchronous functions are there because they can’t live outside the callback function.

Finally, we shouldn’t use addEventListener too often since it keeps adding new event listeners to a DOM object without discarding the old ones. Instead, we can use the set the event listener function to the property of the DOM object which listens to the event we want to listen to. For example, if we want to listen to the click event, then we can set the onclick property of a DOM object with the event handler function that we defined.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *