if a function's lexical environment is created at the time the function is *defined*, then why can a free variable be declared *after* the function?

StackOverflow https://stackoverflow.com/questions/22855278

  •  27-06-2023
  •  | 
  •  

سؤال

EDIT: thanks for the answers, I think I get it now. It requires an understanding of scope and hoisting. Below is a new example that I think illustrates both well:

var a = function (){
    alert(x);
}
var x = 1;

(function(){
    var x = 2;
    a();
})();

The above alerts 1. Lexical scope is illustrated by the fact that this alerts 1 and not 2, and hoisting is illustrated by the fact that the "var x = 1" line comes after the declaration of a and definition of the anonymous function with the "alert(x)". Allegedly, hoisting means that the above is equivalent to the below (source: http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html):

var x;
var a = function (){
    alert(x);
}
x = 1;

(function(){
    var x = 2;
    a();
})();

So since x is effectively initialized before the function definition, the x in "alert(x)" is the same x that subsequently gets set to 1.

Since JS uses lexical scope, the line "var x = 2" does not override the x associated with a.

Is that about right?

---original question---

I've spent pretty much all day trying to figure this out. I've read a few articles on scope and closures in Javascript, but this still eludes me.

I'm told that a function's lexical environment is created when the function is defined, not when it is executed.

So if there is no variable called x declared anywhere in my program, then what is the closure environment for the anonymous function that a points to when I do this?:

var a = func(){
    var y = 7; //just for illustrative purposes
    alert(x);
});

My understanding is that an environment is a mapping of variable names to values...so it would be

y: 7
x: ?

Is that correct? The following code alerts "10":

(function (){
    var a = function(){
        alert(x);
    };
    var x = 10;
    a();
})();

My guess would be that when a is called, the JS checks the closure environment for a mapping for x and finds none, and subsequently checks the local environment and finds the x set to 10 by "var x = 10". Is that right?

If that were the case, then I would expect the following would also work, and yet it does not:

var a = function(){
    alert(x);
};

(function (){   
    var x = 10;
    a();
})();

What I would expect to happen is that, when a is executed, and "alert(x);" is run, it checks the closure environment for x, finds none, then checks the outer environment where "var x = 10" is and finds that x. But instead I get an error that x isn't defined.

Maybe it would help if I could know what 'preparatory' work is done by the interpreter when the anonymous function that a is set to is initially defined--i.e. when the interpreter encounters the x in "alert(x)".

Thanks

هل كانت مفيدة؟

المحلول 2

Think of closure as a stack. Each function has a map/hash in that stack. Here's an example where each level has a value of x that is overwritten by a sub-closure.

function a(){
  var x = 2;
  function b(){
    var x = 3;
    function c(){
      var x = 4;
    }
  }
}

When referencing a value of x, the js processor looks in the current closure. Finding undefined, it walks up the closure stack attempting to find a value. Only when reaching the top level or 'global' scope does it give up and give the value of 'undefined'.

Your example here:

var a = function(){
    alert(x); //no value of x in this closure
};

//no value of x in this closure either

(function (){   
    var x = 10; //this is a completely separate stack
    a(); //no value of 'a' in this closure, but there is one in global scope    
})();

I can see the confusion on the 'a' function not knowing about the 'x' in the anonymous function. Closure scopes are defined 'as written' not 'as called'. You can't inject or mess with variables in a functions closure scope from outside that closure scope. It is one of the things about js that drives me crazy and that I wish were different. I'd so love to have a set of keywords for messing with closure scope...

نصائح أخرى

the JS checks the closure environment for a mapping for x and finds none

In fact it does find one. The position of the var statement does not matter, the name x is still bound to the closure of the outer function. You can think of the declarations being collected during the parsing step and moved to the front (this process is called hoisting).

Thr process of walking up the scopes surrounding an expression in the source code is called lexical binding.

For your second example to work you'd need dynamic binding, which JS does not support (except for globals).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top