2

In es2015, when you using default parameter value, you create an intermediate scope. So I suppose the let x in the function body, create a new variable in a different scope. But why I still get the error?

Using let,I get the error: enter image description here

But Using var, I get two different variables in different scopes. Why? enter image description here

And without var declaration, the result changes. enter image description here

Since they are not in the same scope, let declaration should not throw an error.

T.T
  • 321
  • 2
  • 10
  • @str As you can see in my second pic, the call of the function f returns the value 2, not the value 5, it means the variable x in parameter list is not the same variable x in function body. – T.T May 15 '19 at 07:40
  • Possible duplicate of [How does closure work in function expressions passed as parameters?](https://stackoverflow.com/questions/51633389/how-does-closure-work-in-function-expressions-passed-as-parameters) – str May 15 '19 at 08:31
  • @str Nope. That question is about the parameters are in a different scope. My question is, since they are in different scope, let declaration should not throw an error. But why it throws an error here? – T.T May 15 '19 at 08:57

4 Answers4

1

This is a very interesting question, and I will try my best to give a real answer.

Short answer: formal declarations and let, const declarations are put into one same environment, var declarations will lead to one child environment of it and will inherit initialization value from parent environment.

All these things are for being compatible with history "trouble var", and don't introduce any new trouble, and give formal params with default values one own closure(that is to say, formal params must have one independent environment).

And rethink about it: formal declarations and let, const declarations are put into one same environment. It will work well, because we don't allow redeclaration of let and const.

For details, please see ECMAScript® 2019 Language Specification: sec-functiondeclarationinstantiation

Well, my short answer only works in strict=true condition, but we have a great NOTE:A lexically declared name cannot be the same as a function/generator declaration, formal parameter, or a var name.

Anyway, it is all for being compatible with history trouble var, and don't introduce any new trouble.

  1. If strict is false, then
    1. Let lexEnv be NewDeclarativeEnvironment(varEnv).
    2. NOTE: Non-strict functions use a separate lexical Environment Record for top-level lexical declarations so that a direct eval can determine whether any var scoped declarations introduced by the eval code conflict with pre-existing top-level lexically scoped declarations. This is not needed for strict functions because a strict direct eval always places all declarations into a new Environment Record.
  2. Else, let lexEnv be varEnv.
  3. Let lexEnvRec be lexEnv's EnvironmentRecord.
  4. Set the LexicalEnvironment of calleeContext to lexEnv.
  5. Let lexDeclarations be the LexicallyScopedDeclarations of code.
  6. For each element d in lexDeclarations, do
    1. NOTE: A lexically declared name cannot be the same as a function/generator declaration, formal parameter, or a var name. Lexically declared names are only instantiated here but not initialized.
    2. For each element dn of the BoundNames of d, do
      1. If IsConstantDeclaration of d is true, then
      2. Perform ! lexEnvRec.CreateImmutableBinding(dn, true).
      3. Else,
      4. Perform ! lexEnvRec.CreateMutableBinding(dn, false).
LiuXiMin
  • 1,225
  • 8
  • 17
0

The scope of a parameter(arguments of a function) is the function it is a part of.

function check(param) // it has same scope as the parameters declared inside the curly braces,same like declaring variable inside curly braces
{
 let param=2; // throws error as the param is already been declared in scope (let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used.)

var param=2 ; // does'nt throw error 
console.log(param)
}
check(2)
Shubham Dixit
  • 9,242
  • 4
  • 27
  • 46
  • If it is the same scope. Why when the parameter f get called, return different x as in the three pictures? – T.T May 15 '19 at 08:28
0

this is not related with es2015 default parameter value since it's use is to assign default value to function parameters.

function parameters (in your case x) acts as local variables to that particular function automatically. so variable x was already declared inside that function.

hence at the time of function call like foo(7) you are just assigning the value 7 to variable x (which is already declared inside function).

if you want to change the value of x inside then you can just use like below

function foo(x=2) {
    x=5;
    console.log(x); \\ x will be 5
}

even you are using var keyword to re-declare same variable name (here x), it won't re-declare actually (since it was declared already).

to understand better try like below

function foo(x=2) {
    var x;
    console.log(x); \\ you may expect x to be undefined here but x will be 2 if you call like foo() or x will be 5 if you call like foo(5)
}

summary:

var keyword don't throw any error even you are try to re-declare same variable name as explained above.

but let keyword will throw error when you are trying to re-declare the same variable to avoid such confusions you are facing now.

hope this detailed explanation helps you.

VISWANATH K
  • 126
  • 2
  • Thx. But I still remain one confusion. I just updated the third pic. Why without using `var` in the function body, the x in the function f() changes as well? – T.T May 15 '19 at 08:19
  • function foo(x=2) {} \\ if you call like foo(5) then inside function `var x` will occur **automatically** and then it will assign `x=5`. if you call like foo() then inside function `var x` will occur **automatically** and then it will assign `x=2` (since if you are not passing any value then it will take default parameter value). since **x** is already declared inside that function, you can change **x** variable value like **x=5** (what you did in your third picture). pls refer next comment too – VISWANATH K May 15 '19 at 09:18
  • since **x** is already declared inside that function, then why you try to declare again like **var x** ? even you are using `var x` again inside that function there is no effect because **x** was already declared. below both are same: { var x = 5; x = 7; } { var x = 5; var x = 7; \\ here no effect in using **var** } – VISWANATH K May 15 '19 at 09:18
  • 1
    You said declare x again in the function body makes no difference, but the parameter f of the function foo returns different value of x(as in the second picture, f() returns 2 but in third picture, f() returns 5), why is that happening? – T.T May 15 '19 at 09:23
0

We can think as all variables inside a function are in the same scope. In your example, it's giving error in case of let because let/const re-declaration in the same scope gives error, but not in case of using var.

We may see this in mdn saying:

"Duplicate variable declarations using var will not trigger an error, even in strict mode, and the variable will not lose its value unless another assignment is performed."

rensothearin
  • 670
  • 1
  • 5
  • 24