---
title: "Reading the ECMA-262: How Declarations in Block Context Work"
description: "To understand hoisting and variable scoping better, let's dive into the ECMAScript specification of block declarations."
lang: "en"
tags: ["JavaScript", "ECMA-262"]
pubDate: 2024-07-01T00:00:00.000Z
---

There are four kinds of context in which variable or function declarations would occur: Global, module, function, and block. This article focuses on the **block** context.

## Steps in Specification of Declarations in Block Context

The specification of [`Evaluation`](https://262.ecma-international.org/13.0/index.html#sec-block-runtime-semantics-evaluation) of blocks and [`BlockDeclarationInstantiation`](https://262.ecma-international.org/13.0/index.html#sec-blockdeclarationinstantiation) are not as complicated as the function or global ones. So we can look into the source of specification directly:

> **14.2.2 Runtime Semantics: Evaluation**
> Block : { StatementList }
>
> 1. Let `oldEnv` be the running execution context's LexicalEnvironment.
> 2. Let `blockEnv` be `NewDeclarativeEnvironment(oldEnv)`.
> 3. Perform `BlockDeclarationInstantiation(StatementList, blockEnv)`.
> 4. Set the running execution context's LexicalEnvironment to `blockEnv`.
> 5. Let `blockValue` be the result of evaluating StatementList.
> 6. Set the running execution context's LexicalEnvironment to `oldEnv`.
> 7. Return `blockValue`.

> **14.2.3 BlockDeclarationInstantiation ( code, env )**
> The abstract operation BlockDeclarationInstantiation takes arguments code (a [Parse Node](https://262.ecma-international.org/13.0/index.html#sec-syntactic-grammar)) and env (a [declarative Environment Record](https://262.ecma-international.org/13.0/index.html#sec-declarative-environment-records)) and returns unused. code is the [Parse Node](https://262.ecma-international.org/13.0/index.html#sec-syntactic-grammar) corresponding to the body of the block. env is the [Environment Record](https://262.ecma-international.org/13.0/index.html#sec-environment-records) in which bindings are to be created.
> Note: When a _Block_ or _CaseBlock_ is evaluated **a new declarative Environment Record** is created and bindings for each block scoped variable, constant, function, or class declared in the block are instantiated in the Environment Record.
>
> 1. “ Let `envRec` be env's EnvironmentRecord.
> 2. Assert: `envRec` is a declarative Environment Record.
> 3. Let `declarations` be the **LexicallyScopedDeclarations of code**.
> 4. For each element `d` in declarations, do
>    For each element `dn` of the BoundNames of `d`, do
>    If IsConstantDeclaration of d is true, then
>    Perform ! `envRec.CreateImmutableBinding(dn, true)`.
>    Else,
>    Perform ! `envRec.CreateMutableBinding(dn, false)`.
>    If d is a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration, then
>    Let `fn` be the sole element of the BoundNames of d.
>    Let `fo` be InstantiateFunctionObject of `d` with argument `env`.
>    Perform `envRec.InitializeBinding(fn, fo)`.

According to the spec, `BlockDeclarationInstantiation` does not include `var` statements (VariableStatement), as these are not part of `LexicallyScopedDeclarations`. The `LexicallyScopedDeclarations` include `let`, `const`, `function`, and `class`.

As mentioned in the previous articles, the VariableStatements in nested blocks have been collected into VarScopedDeclarations when processing global or function declaration instantiations.

And the `BlockDeclarationInstantiation` in the block are bonded to the newly created declarative Environment Record, whose environment’s outer environment is the running execution context's LexicalEnvironment

## Test in the Hosts

We can test the behavior in actual hosts. The following pieces of code are tested in the hosts with the same result:

- Edge 127.0.2651.86
- Firefox 129.0
- Safari 17.2.1
- Node v18.17.1

### `function` declarations

`function` declarationsinside the block should NOT be hoisted out of that block:

- In a global context:

  ```jsx
  "use strict";

  foo("before block"); // ReferenceError: foo is not defined
  {
    foo("in block before declaration");
    function foo(position) {
      console.log("Block scoped function", position);
    }
    foo("in block after declaration");
  }
  foo("after block");
  ```

- In a function context:

  ```jsx
  "use strict";

  function test() {
    foo("before block"); // ReferenceError: foo is not defined
    {
      foo("in block before declaration");
      function foo(position) {
        console.log("Block scoped function", position);
      }
      foo("in block after declaration");
    }
    foo("after block");
  }

  test();
  ```

### `var` declarations

`var` declarationsinside the block should be hoisted out of that block and initialized with value `undefined`:

- In a global context:

  ```jsx
  "use strict";

  console.log("before block", bar); // undefined
  {
    console.log("in block before declaration", bar); // undefined
    var bar = 30;
    console.log("in block after declaration", bar); // 30
  }
  console.log("after block", bar); // 30
  ```

- In a function context:

  ```jsx
  "use strict";

  function test() {
    console.log("before block", bar); // undefined
    {
      console.log("in block before declaration", bar); // undefined
      var bar = 30;
      console.log("in block after declaration", bar); // 30
    }
    console.log("after block", bar); // 30
  }

  test();
  ```
