loading...
Cover image for Constants In JavaScript Switch

Constants In JavaScript Switch

rfornal profile image bob.js ・3 min read

I recently added a feature to some client code. I won't show the code here, but it was a simple copy-paste with a minor set of changes to ensure the new code executed. The code failed miserably and ran me down an interesting rabbit hole.

The original version of the code when I began working on this project was a long IF-THEN, ELSE-IF block. It made sense to shift to a SWITCH statement ... making the code easier to read and understand.

I ran into an issue that is one of those, "I knew that" moments where my understanding of JavaScript's internal workings went up another notch.

Original Code

The original IF-ELSE, ELSE-IF block went something like this ...

const demoSwitch = () => {
  const demo = {
    first: 1
  };

  if (demo.first === 1) {
    const result = 1;
    return result;
  } else if (demo.second === 2) {
    const item = 2;
    return item;
  } else {
    return -1;
  }
};
Enter fullscreen mode Exit fullscreen mode

Now, this example is super-simplified, to allow this article to focus on the true issue. There were specific reasons in the original code why result and item were used. This allowed the first round of refactoring to work correctly, as we'll see later.

Conversion to Switch

With a basic understanding of the code above, it got converted to something like this ...

const demoSwitch = () => {
  const demo = {
    first: 1
  };

  switch (demo.first) {
    case 1:
      const result = 1;
      return result;
    case 2:
      const item = 2;
      return item;
    default:
      return -1;
  }
};
Enter fullscreen mode Exit fullscreen mode

Looking at this code, an experienced developer would begin to question some of the decisions that lead to the return lines within the CASES. However, this is simplified code and in its original form, the code has good reason the maintain this form.

Breaking the Switch

Now, as was said earlier, a new feature was added that paralleled another existing feature. The switch above needed an additional case.

This is where the problem started (and ended).

const demoSwitch = () => {
  const demo = {
    first: 1
  };

  switch (demo.first) {
    case 1:
      const result = 1;
      return result;
    case 2:
      const item = 2;
      return item;
    case 3:
      const result = 3;
      return result;
    default:
      return -1;
  }
};
Enter fullscreen mode Exit fullscreen mode

This code (before it is even executed) returns the following error: Uncaught SyntaxError: Identifier 'result' has already been declared.

I was stumped for a minute, tried a minor adjustment to the code.

const demoSwitch = () => {
  const demo = {
    first: 1
  };

  let result = -1;
  switch (demo.first) {
    case 1:
      result = 1;
      return result;
    case 2:
      const item = 2;
      return item;
    case 3:
      result = 3;
      return result;
    default:
      return result;
  }
};
Enter fullscreen mode Exit fullscreen mode

This worked.

Here's another pattern suggested in the comments ...

const demoSwitch = () => {
  const demo = {
    first: 1
  };

  switch (demo.first) {
    case 1: {
      const result = 1;
      return result;
      }
    case 2: {
      const item = 2;
      return item;
      }
    case 3: {
      const result = 3;
      return result;
      }
    default:
      return -1;
  }
};
Enter fullscreen mode Exit fullscreen mode

This pattern works, as well!

Thanks to Kostia Palchyk.

Conclusion

Basically, this issue was about scope.

As a reminder:

  • Declaring a variable using var uses the function-level scope.
  • Declarations using let and const are block-level scoped (think, wrapped in parentheses {}).

If the variable result had been declared using:

  • var, it would have been hoisted and redeclaration would have occurred later in the code.
  • let, declaration and redeclaration at the block-level would have occurred.

The variable(s) were declared using const, and therefore could not be redeclared at the same block-level.

While this seems like a simple thing, it is one of those little issues that can cause some consternation when a developer comes across it.

Discussion

pic
Editor guide
Collapse
kosich profile image
Kostia Palchyk

I think, you can wrap case body in {…}, e.g:

function demo () {
  const demo = {
    first: 1
  };

  switch (demo.first) {
    case 1: {
      let result = 1;
      return result;
    }
    case 2: {
      let result = 2;
      return result;
    }
    case 3: {
      let result = 3;
      return result;
    }
    default:
      return demo.first;
  }
}

--

Also, there's a hack that always blows my mind:

(not for production imho, but interesting)

switch (true) {
  case (demo.first == 1) {}
  case (demo.first == 2) {}
  case (callSomeFunction(demo.first)) {}
}
Collapse
rfornal profile image
bob.js Author

Interesting.

  • I'll add the first case to my notes.
  • The second case is one I love to use. I think it actually makes it more readable.
Collapse
rfornal profile image
bob.js Author

Added the pattern to the main article!