3

I'm a bit confused about how scope & variable assignment within a function seems to change when assigning a default parameter value for that function.

For example, when this function has a default value assigned to parameter i, the output array variable appears to be block-scoped when inspected with Chrome Dev Console,:

function steps(n, i = 40) {
 var output = [n];
}

steps(10, 20);

a

However, by removing the default parameter value for i, the output array variable is scoped locally:

function steps(n, i) {
  var output = [n];
}

steps(10, 20);

enter image description here

Why does assigning a default value to parameter i affect the scope of the output array variable?

I was initially made aware of this shift in function scope by attempting to run the following segment of code through pythontutor.com's live programming environment for Javascript. Even though the code executes as expected within an IDE, it fails to run due to scope issues on pythontutor:

function steps(n, i = 1) {
  // declare base case
  if (n === 0) 
    return;

  var output = [];
  print(i, "#");
  print(n - 1, " ");
  console.log(output.join(""));

  // make recursive call
  steps(n - 1, i + 1);

  function print(num, char) {
    for (let j = 0; j < num; j++) {
      output.push(`${char}`);
    }
  }
}


steps(3);  

The pythontutor processor halts execution three steps in, at the invocation of print() just after declaring the output variable. Pythontutor.com will, however, execute the code as expected if I declare the output variable globally first:

var output = [];

function steps(n, i = 1) {
  // declare base case
  if (n === 0) 
    return;

  output = [];
  print(i, "#");
  print(n - 1, " ");
  console.log(output.join(""));

  // make recursive call
  steps(n - 1, i + 1);

  function print(num, char) {
    for (let j = 0; j < num; j++) {
      output.push(`${char}`);
    }
  }
}


steps(3);
  • 1
    are you using babel by any chance? If it's being rewritten, it's likely changing how the parameters are accessed. – zzzzBov Jan 18 '18 at 22:32
  • Scratch that, just ran the test code in the chrome dev tools directly and sure enough the scope *does* change depending on whether or not there's a default parameter defined for the function. – zzzzBov Jan 18 '18 at 22:42

1 Answers1

1

It's because default initialisers run in their own scope. Only if there are none, the body code is evaluated in the top function scope. It would only make a difference if you put a function expression in a default initaliser, which may close over the other parameters but does not have access to the variables that will be declared in the body.

Basically it's the difference between

function steps() {
  var n = arguments[0],
      i = arguments[1];
  var output = [n];
}

and

function steps() {
  var n = arguments[0],
      i = arguments.length > 0 ? arguments[1] : 40;
  (() => {
    var output = [n];
  }());
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375