1

A thing about closure. In these two snippets below I'm passing a function expression as a callback.

(In the first snippet) When calling back the function expression, I expect to see the anonymous function to close over def, but when I call second() instead of looking inside its "closure" for the variable first (where first is updatedValue), search and find first in the global variable environment, where first has the value oldValue.

function def(first="oldValue" , second=function(){
         return first;
}){
        var first="updatedValue";
        console.log('inside',first);
        console.log('function',second());
}


//OUTPUT: 
inside updatedValue
function oldValue

On the other hand, if you don't declare first inside def, it second() console log updatedValue

function def(first="oldValue" , second=function(){
         return first;
}){
        first="updatedValue";           // NOTE: removed the `var` here
        console.log('inside',first);
        console.log('function',second());
}
//OUTPUT:
inside updatedValue
function updatedValue

Can someone explain what's going on here?

leonardofed
  • 936
  • 2
  • 9
  • 24
  • You're not *passing the closure as a parameter*. You are defining a default initialiser for the parameter that gets used when nothing is passed. – Bergi Aug 01 '18 at 12:49

1 Answers1

1

As quoted from the ES specs here, using a default parameter creates a new scope in which the default parameters reside, then another scope in which the function body resides. That means that first refers to the variable in the enclosing default parameter scope, while var first shadows it in the functions scope.

Your code basically is the same as:

function def(_first, _second) {
  // Scope of the default parameters
  let first = _first ?? "default";
  let second = _second ?? function(){
     return first;
  };
  (function() {
     // The functions scope          
     var first = "updatedValue"; // scoped here
     console.log('inside',first);
    console.log('function',second());
  })();
}
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • Hi @JonasWilms , could you please also explain why 2 separate scopes are created for the 2 parameters with default values, apart from the function body scope ? In all the examples in the link you shared https://stackoverflow.com/questions/44896829/scope-of-default-function-parameters-in-javascript, only 1 scope is created for all parameters with default values. Also in this answer of yours parameters are declared using var but in the link you provided parameters are declared using let after de-sugaring, so how is var or let determined ? – Dhiren Punjabi Jun 09 '23 at 08:47
  • 1
    @DhirenPunjabi this answer is slightly incorrect, technically there is only one environment record in which the default parameters are evaluated, though as parameters are only bound after being evaluated, later parameters are not visible in the evaluation of the default expression (c.f. https://262.ecma-international.org/6.0/#sec-functiondeclarationinstantiation 28.f) – Jonas Wilms Jun 09 '23 at 16:45
  • So yeah, using one invoked function with let is a more accurate syntactic sugar. – Jonas Wilms Jun 09 '23 at 16:46
  • As per this answer of yours to the question https://stackoverflow.com/questions/56301703/why-is-there-still-a-duplicate-error-even-when-i-declare-the-variable-in-2-diffe , the parameter names also exist in the scope of the function body and if the parameters are declared by let (as per your above reply to my comment) then it might cause a redeclaration error. So it looks like parameters are declared with var by default and not let, for every javascript function. Please correct me if I'm wrong – Dhiren Punjabi Jun 10 '23 at 02:36
  • @DhirenPunjabi parameters and let and const variables are three entirely different things with varying semantics, all of which can be described by environment records and bindings. – Jonas Wilms Jun 10 '23 at 09:11