5

TLDR: Why are function parameters not treated as var declarations?

Using var, redeclaring a variable declared using var has no effect. The original declaration remains in effect.

However, using var in this way will mask a parameter of the same name.

Why is the language designed like this?

In the following, variable var bar is treated as a new declaration and not a redeclaration of parameter bar.

function foo(bar = 0, bam = () => bar) {
  var bar = 1
  return bam()
}
console.log(foo()) // 0

Is it designed like this because arguments and local variables are semantically fundamentally different, and fall into two categories?

I might have expected parameters to be treated like var declarations, but they aren't. They appear to be in their own "box" on the scope chain.

So the scope chain of a function looks like this:

function body >> parameter list >> outer function body >> outer parameter list >> ... global scope
Ben Aston
  • 53,718
  • 65
  • 205
  • 331
  • This is the kind of counter-intuitive behaviours that recent language additions have been addressing. If you declare variables with `let` you get `SyntaxError: redeclaration of formal parameter bar` – Álvaro González May 04 '20 at 08:30
  • It looks like ES2015 introduced this. – Ben Aston May 04 '20 at 08:59
  • @ÁlvaroGonzález there is no issue there, it's the expected behaviour. You are re-initialising a binding for the current scope in that case. You'd get the same if you had `var foo; let foo;` – VLAZ May 04 '20 at 12:49

2 Answers2

3

Are arguments and local variables semantically fundamentally different, and fall into two categories?

No, not really. In fact, in many cases they are indistinguishable, creating variables in the same scope - just not here in the example you gave.

I might have expected parameters to be treated like var declarations, but they aren't. They appear to be in their own "box" on the scope chain.

Yes. That's because you are using default initialisers in your parameter declaration, which gives the parameters their own scope, and the function body scope is a child of that. Still, vars are getting declared in that body scope (not getting hoisted).

It might be fitting to say that parameters are treated like let declarations since ES6, except where they need to be treated as var declarations to preserve backwards compatibility (allowing re-declarations). See here or there for details.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • OK, so this behavior is an artifact of the addition of default arguments to the language? – Ben Aston May 04 '20 at 08:36
  • 1
    @BenAston I wouldn't say "artifact", it seems deliberate. Yes, before the addition of the parameter defaults the scopes could not be distinguished at all. Now since we have them, `let` semantics with TDZ and everything were chosen for the initialisation of parameters. – Bergi May 04 '20 at 08:44
1

The concept of variable hoisting and clouser in javascript states that variables remembers enviornment in which they are created.

Now lets have a look in your example line by line

function foo(bar = 0, bam = () => bar) { // here bar 0, and function `bam` will remember bar value as 0 whenever it gets called

And when you are reassigning bar to 1 in function foo, function bam will not update it. As both function have different scopes.

So as per the javascript concept, this is correct and desired behaviour.

Update:

function foo(bar = 0, bam = () => bar) {
   bar = 1
   return bam()
}
console.log(foo()) // 1
Kiran Shinde
  • 5,732
  • 4
  • 24
  • 41
  • The arrow function's closure will not remember the value of `bam`, but maintain a (indirect) reference to the actual parameter. – Ben Aston May 04 '20 at 08:21
  • 2
    The question is *why* they have different scopes. The scoping with parameters is a bit unintuitive, so it's not clear the reason `bam` has different scope to `foo` – VLAZ May 04 '20 at 08:25
  • Agreed but for that you are redeclaring `bar` in your function. which will have local scope. If you don't declare it, then it will maintain reference. See my updated example – Kiran Shinde May 04 '20 at 08:26