user2076675
user2076675

Reputation:

When I make a reference to a global variable, why does the closure not reflect a change to the original?

In an app I'm developing, I define a global App namespace, where I store some properties that I want to access from different functions. For example I might keep a menu menuOpen property stored in my global App namespace so the function that I use to handle the Menu interface functionality can share this information easily with a different function that handles something else.

I was having a problem with this a moment ago, after updating this global variable with one function, and then checking a reference to it in the closure of another function, to find the reference didn't reflect my change.

I re-recreated this in a simple example, where I would expect false to be the result, since bar changes open to false, which should be reflected by state within foo's closure before the timeout completes and runs the check against state:

//set a global variable to be accessed by different parts 
//of an application
var open = true;

//create a closure, which waits for a future event, 
//then checks the "open" variable when it occurs
function foo() {

  //reference to the global variable "open"
  var state = open;

  //set a timeout, to reflect "a future event", such 
  //as an event handler
  setTimeout(function() {
    if (state) {
      $('html').text('true');
    } else {
      $('html').text('false');
    }
  }, 1000);
}

//change the "open" global within another function
function bar() {
  open = false;
}

//create the closure
foo();

//change "open" to false, before the "if(state)" logic 
//is called in the closure
bar();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

It was my understanding that a closure keeps a reference to a variable, so state would reflect any change to open. How does this actually work? And what would I need to do differently to make foo's closure aware of open's change?

Upvotes: 0

Views: 188

Answers (2)

jfriend00
jfriend00

Reputation: 707238

As others have said, this issue doesn't have anything to do with closures. It has to do with how Javascript does assignment of different types.

Only objects in Javascript are assigned by pointer. All other data types are assigned by value (e.g. the value is copied). So, when you do:

var state = open;

And, open is anything other than an object (which includes objects, arrays and functions), then the value in open is copied to state and the two variables then have nothing to do with one another.

See this example:

var myNum = 12;
var otherNum = myNum;
document.write("myNum = ", myNum, "<br>");          // 99
document.write("otherNum = ", otherNum, "<br>");    // 12

myNum = 99;
document.write("myNum = ", myNum, "<br>");          // 99
document.write("otherNum = ", otherNum, "<br>");    // still 12

But, if you assigned an object, it would just be a pointer to the original object so if the original was changed, you would see that change in the other reference to that same object:

function log(name, obj) {
    document.write(JSON.stringify(obj) + "<br>");
}

var myObj = {greeting: "hello"};
var otherObj = myObj;
log("myObj", myObj);
log("otherObj", otherObj);

myObj.greeting = "bye";
log("myObj", myObj);
log("otherObj", otherObj);

Objects are assigned by pointer to the original. All other types of variables behave as if they are copied and no longer have a connection to the original.

If you want to take a normal non-object variable and get it to behave like you have a pointer to it, then you can put that variable into an object as a property on that object and you can pass the object around. Then, everyone who you pass the object to can examine the property and they will all be looking at exactly the same property value. If one changes it, they will all see the changed value.

Upvotes: 0

melpomene
melpomene

Reputation: 85767

This has nothing to do with closures. var x = "a"; var y = x; x = "b"; console.log(y) outputs "a", not "b", because y is a copy of x.

Closures don't change the normal behavior of variables. A closure is simply a function that uses local variables from a surrounding scope. foo is a closure because it uses open; the function passed to setTimeout is a closure because it uses state (but state never changes after being set).

You can fix your code by checking if (open) directly.

Upvotes: 2

Related Questions