5

const show1 = function(x, y = () => {x = 2; return x;}) {
    let x = 3;
    console.log(y());
    console.log(x);
};
show1();

const show2 = function(x, y = () => {x = 2; return x;}) {
    x = 3;
    console.log(y());
    console.log(x);
};
show2();

const show3 = function(x, y = () => {x = 2; return x;}) {
    var x = 3;
    console.log(y());
    console.log(x);
};
show3();

output

show1: Uncaught SyntaxError: Identifier 'x' has already been decalred;
show2: 2 2
show3: 2 3

Question

I was informed that there is a temporary dead zone where parameter variables are declared and initialized. See https://exploringjs.com/es6/ch_variables.html#sec_parameters-as-variables. So there are two scopes here, one is the parameter scope and the other is function scope.

  1. From the error in show1, I thought that there is a x variable already declared in this function.
  2. According to Redeclaring a javascript variable. The re-declaration won't do anything to x(with var). Why the results of show2 and show3 are different.

I posted the same question here If the variables are already declared at the beginning of function execution which was masked as duplicate. But I couldn't find anything useful for my doubt.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
BAKE ZQ
  • 765
  • 6
  • 22
  • *According to Redeclaring a javascript variable. The re-declaration won't do anything to x* See https://stackoverflow.com/a/58219129/ , that isn't correct when using modern syntax (`const` and `let`), which none of the other answers there have mentioned; if a variable name is declared with `const` or `let`, it must not be initialized elsewhere (such as in the parameter list, or with `var` elsewhere in the block) – CertainPerformance Oct 03 '19 at 12:31
  • @CertainPerformance That's the case in show1. What about the difference in show2 and show3? – BAKE ZQ Oct 03 '19 at 12:32
  • @BAKE ZQ see my answer – degr Oct 03 '19 at 12:41

2 Answers2

4

show1 throws an error because variables declared with let or const cannot have any other variables with the same name initialized in that block (whether in an argument list or with const / let / var).


Variables referenced by default parameters have odd scoping rules. Every argument essentially creates another block that that argument name can be defined in. So

const show3 = function(x, y = () => { x = 2; return x; }) {

is somewhat like (forgive the psuedo-code):

const show3 = < function >{
  let x = firstArg;
  {
    let y = secondArg === undefined ? () => { x = 2; return x; } : secondArg;
    {
      // function body

When you simply assign to a variable name in the argument list, that argument will be overwritten. But when you use syntax to initialize a new variable (with var), you've created another binding for that variable name, which is only visible inside the function body:

const show3 = {
  let x = firstArg;
  {
    let y = secondArg === undefined ? () => { x = 2; return x; }
    // the x referenced in the above line references the outer x, the first argument
    {
      // function body

      // since "var x" is declared here, any references to "x" in this block
      // will only reference the inner x
      // but not the outer x
      var x = /* something */

So, your show2 is reassigning the parameter named x to 3 in the first line of the function body. In contrast, show3 is creating a new variable binding with the same name of x, while the y function's reference to x is referencing the argument x, which is different.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • If you mean that there is no `x` declared in this function. I am referring the argument `x` in the parameter scope? – BAKE ZQ Oct 03 '19 at 12:49
  • Pretty much - the scope of a variable in the parameter list is (very slightly) different from the scope of a variable declared in the function body, though it'll rarely have a noticeable effect on code. – CertainPerformance Oct 03 '19 at 12:51
  • What if there are no default values for parameters. If this parameter scope still exists? And with the assumption that these two scopes are different, why I cant' use `let` or `const` here? (Since they are in different blocks. I can write codes like `{let a = 0; { let a = 0}}`) – BAKE ZQ Oct 03 '19 at 12:54
  • If there aren't any default parameters, then if there's an argument `x`, `var x =` will be indistinguishable from `x =`, because there are no scopes other than the function body which can reference the *argument alone*. I'm sure the specification describes the precise behavior, but whatever it is, it's not observable – CertainPerformance Oct 03 '19 at 13:01
  • For whatever reason, it just happened to be decided that variables declared with `let` and `const` shadowing an argument variable name would throw an error (probably because it results in confusing semantics), even though it'd *look* like such a thing would be OK from the psuedo-code above – CertainPerformance Oct 03 '19 at 13:02
1

Why the results of show2 and show3 are different.

let's evaluate your code by this way

const show2 = function(x, y = () => {x.value = 2; return x;}) {
    x = {name: "from argument", value: 3};
    console.log(y());//{name: "from argument", value: 2}
    console.log(x);//{name: "from argument", value: 2}
};
show2();

const show3 = function(x, y = () => {if(!x){x = {name:"from function", value: -1}}x.value = 2; return x;}) {
    var x = {name: "from var", value: 3};
    console.log(y());//{name: "from function", value: 2}
    console.log(x);//{name: "from var", value: 3}
};
show3();

const show4 = function(x, y = () => {if(!x){x = {name:"from function", value: -1}}x.value = 2; return x;}) {
    var x = {name: "from var", value: 3};
    console.log(y());//{name: "from outside", value: 2}
    console.log(x);//{name: "from var", value: 3}
};
show4({name:"from outside", value: -1})
degr
  • 1,559
  • 1
  • 19
  • 37
  • Thx. Then I was wrong. There is only a `x` variable in arguments. Right? The `x` in in show2 is actually the result of looking variable in argument list(or scope) – BAKE ZQ Oct 03 '19 at 13:10
  • For case 2 look like so. As I understand, there is 3 scopes `{global{function{scope of function arguments {scope of function body}}}}` – degr Oct 03 '19 at 13:12
  • It makes sense now. But how to explain the peculiar behavior of `let`. Or it's because `It happend to be` said by @Certainperformance – BAKE ZQ Oct 03 '19 at 13:14
  • Yes, should be. I can't explain it now, currently your question stops work of all our department – degr Oct 03 '19 at 13:22