2

When I try to run the foo function defined in this snippet I get a ReferenceError since b is not defined.

var b = 3;

function foo( a = 42, b = a + b + 5 ) {
    // ..
}
foo()

This looks like a TDZ error because b has been defined in the outer scope but it's not yet usable in the function signature as a right-hand-side value.

This is what I think the compiler should do:

var b;
function foo(..) { .. }

// hoist all functions and variables declarations to the top

// then perform assignments operations

b = 3;
foo();

//create a new execution environment for `foo`
// add `foo` on top of the callstack

// look for variable a, can't find one, hence automatically create a 
   `var a` in the local execution environment and assign to it the 
    value `42`
// look for a `var b` in the global execution context, find one, use 
   the value in it (`3`) as a right-hand-side value.

This shouldn't raise a ReferenceError. Looks like this is not what happens here.

Can someone explain in what actually does the compiler do and how it processes this code?

leonardofed
  • 936
  • 2
  • 9
  • 24

2 Answers2

2

On every function call, the engine evaluates some prologue code, which contains formal parameters, declared as let vars and initialized with their actual values or default expressions, if provided:

var b = 3;

function foo( ) {
    let a = <actual param for a> OR 42;
    let b = <actual param for b> OR a + b + 5;
   // ..
}

Since b in a function is lexical (let), it's not possible to access its value before initialization. Hence the ReferenceError.

Note that this is a call-time error, so the following compiles fine:

var b = 1

function foo(b=b) {
  console.log(b)
}

The error happens when you actually call the function:

var b = 1

function foo(b=b) {
  console.log(b)
}

foo() 

and only when the engine actually evaluates the faulty default expression:

var b = 1

function foo(b=b) {
  console.log(b)
}

foo(7) 

ECMA standard reference: FunctionDeclarationInstantiation, p.21:

For each String paramName in parameterNames, do

...Perform ! envRec.CreateMutableBinding(paramName, false).

georg
  • 211,518
  • 52
  • 313
  • 390
2

The function arguments somewhat works like 'let'.

We cannot access variable created using 'let' before they are declared .i.e variables created using 'let' are not hoisted.

This happens because if the we are declaring the variable in local scope , it cannot access the global scope variable(Unless 'this' is used ) Your code can be fixed by this -

var b = 3;

function foo( a = 42, b = a + this.b + 5 ) {
    // default binding. In this case this.b = global var
}
foo()

you can also see this error if you do this.

let variable = variable;
Komal Bansal
  • 789
  • 2
  • 7
  • 20
  • 1
    *"g is undeclared at the time we are trying to create a variable with same name using 'let'."* That's not quite right. It is actually declared, but the declaration is *not initialized*, which causes the error. `var` declarations OTOH are always initialized with `undefined`. – Felix Kling Dec 31 '18 at 06:48
  • Yes , you are right. – Komal Bansal Dec 31 '18 at 06:56