Categories
JavaScript Mistakes

JavaScript Mistakes — Expressions

Spread the love

JavaScript is a very forgiving language. It’s easy to write code that runs but has mistakes in it.

In this article, we’ll look at some confusing expressions that we shouldn’t be writing in JavaScript code.

Confusing Multiline Expressions

JavaScript has the automatic semicolon insertion(ASI) feature which adds semicolons automatically.

Therefore, we can omit the semicolon and still have a valid JavaScript code. However, this doesn’t mean that they’re easy to read for users.

In JavaScript, a newline character always ends a statement like with a semicolon except when:

  • The statement has an unclose parentheses, array literal, object literal or ends in some other way that’s not a valid way to end a statement
  • The lines is -- or ++
  • It’s a for , while , do , if , or else and there’s no (
  • The next lines start with arithmetic or other binary operators that can only be found between 2 operands

There’re cases where multiline expressions where the new line looks like it’s ending a statement, but it’s not. For instance, the following aren’t are multiline but are actually one expression:

let b = 3
let a = b
(1 || 2).c();

We’ll get a ‘b is not a function’ message since the last 2 lines are interpreted as:

let a = b(1 || 2).c();

Another would be the following:

let addNumber = ()=>{}
let foo = 'bar'
[1, 2, 3].forEach(addNumber);

The code above would get us syntax errors. Therefore, we should put semicolons at the end of each statement so that no developer or JavaScript interpreter would be confused or give errors.

Unreachable Code After return, throw, continue, and break statement

Unreachable code after return , throw , break , and continue are useless because these statements unconditionally exits a block of code.

Therefore, we should never have code that’s never going to be run after those lines.

For instance, the following function has unreachable code:

const fn = () => {
  let x = 1;
  return x;
  x = 2;
}

x = 2 is unreachable since comes after the return statement.

Other pieces of code that we shouldn’t write include:

while(true) {
    break;
    console.log("done");
}

const fn = () => {
  throw new Error("error");
  console.log("done");
}

Therefore, we should write:

const fn = () => {
  let x = 1;
  return x;
}

Control Flow Statements in finally Blocks

In JavaScript, the finally block is always run before the try...catch block finishes when it’s added after a try...catch block. Therefore, whenever we have return , throw , break , continue in finally , the ones in try...catch are overwritten.

For instance, if we have:

let x = (() => {
  try {
    return 'foo';
  } catch (err) {
    return 'bar';
  } finally {
    return 'baz';
  }
})();

Then x would be 'baz' since the finally block’s return statement runs before the ones in the try...catch block.

Therefore we shouldn’t add flow control statements to the finally block since it made the ones in try...catch useless. We should instead write something like:

let x = (() => {
  try {
    return 'foo';
  } catch (err) {
    return 'bar';
  } finally {
    console.log('baz');
  }
})();

so that the return statements in try...catch have a chance to run.

Negating the Left Operand of Relational Operators

Negating the left operand doesn’t always negate the whole operation expression in JavaScript.

For instance:

!prop in object

only negates prop and:

!foo instanceof C

only negates foo .

Therefore, !prop in object is actually true in object or false in object depending on the truthiness of prop .

Likewise, !foo instanceof C is actually, true instanceof C or false instanceof C depending on the truthiness of foo.

Therefore, we should wrap the whole expression in parentheses so it actually negates the return value of the whole expression as follows. Therefore:

!(`prop in object)`

and:

!(`foo instanceof C)`

are what we want.

This way, there’s no confusion about what we’re trying to do with those expressions.

Conclusion

There’re many ways to create confusing expressions with JavaScript. One way is to omit semicolons at the end of the line. Omitting semicolons can create ambiguous expressions easily.

We can also create useless code by writing unreachable expressions. They’re useless so they shouldn’t be written. This include writing code after return , break , throw , and continue . Also, writing those statements in finally also make the ones in try...catch useless.

Finally, negating the left operand of the in and instanceof operators only negate the left operand rather than the whole expression.

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 *