Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

switch statement and scopes in ES2015

Consider this ES2015 module and the behavior when run in node v4.4.5.

'use strict'

const outer = 1

switch ('foo') {
  case 'bar':
    const heyBar = 'HEY_BAR'
    break
  case 'baz':
    const heyBaz = 'HEY_BAZ'
    break
  default:
    const heyDefault = 'HEY_DEFAULT'
}
console.log(
  outer, // 1, makes sense, same top-level scope
  heyBar, // undefined. huh? I thought switch did NOT create a child scope
  heyBaz, // undefined. huh? I thought switch did NOT create a child scope
  heyDefault) // 'HEY_DEFAULT' makes sense

This seems internally inconsistent to me. If the switch statement did NOT create a lexical scope, I would expect all the hey* variables to be part of the main scope and to all behave consistently. If the switch statement did create a lexical scope, I would still expect them to be consistent, but the variables declared in the case clauses behave like they are in a child scope, whereas the variables in the default clause behaves like it is in the outer scope.

My question is are there any child scopes involved in a switch statement, and if so what are the details of how they behave?

In node v6.4.0, the behavior is different. It looks like the switch block does indeed create a child block scope.

ReferenceError: heyBar is not defined

And that seems a lot more straightforward to understand.

like image 768
Peter Lyons Avatar asked Dec 04 '22 00:12

Peter Lyons


1 Answers

I can't reproduce your behavior at all. I immediately get a ReferenceError (Node 6.4.0 and current Firefox for that matter):

ReferenceError: heyBar is not defined

Which seems like the correct behavior to me. AFAIK switch statements with brackets DO create a block, and thus a lexical scope for block-scoped entities. The case statements themselves do not create their own blocks.

If we expand this example with a foo case in the switch statement, it throws a ReferenceError as well:

'use strict'

const outer = 1

switch ('foo') {
  case 'bar':
    const heyBar = 'HEY_BAR'
    break
  case 'baz':
    const heyBaz = 'HEY_BAZ'
    break
  case 'foo':
    const heyFoo = 'HEY_FOO'
    break
  default:
    const heyDefault = 'HEY_DEFAULT'
}
console.log(
  outer,
  heyFoo,
  heyBar,
  heyBaz,
  heyDefault) // ReferenceError: heyFoo is not defined

Reference

Here is the section in the spec: 13.12.11 Runtime Semantics: Evaluation

5. Let blockEnv be NewDeclarativeEnvironment(oldEnv).
6. Perform BlockDeclarationInstantiation(CaseBlock, blockEnv).

Where CaseBlock is the block statement of the switch case.

This roughly translates to:

Create a new block environment in the block of the switch statement (switch { <-here-> }) and instantiate all block level declarations (such as let, const or block level function declarations).

like image 178
nils Avatar answered Dec 24 '22 21:12

nils