1

I have a program like this:

var newData={};
for(var i=0;i<3;i++){
    newData.value=i;
    foo(newData);
}
function foo(data){
    setTimeout(function(){
        console.log(data.value)
    },1000)
}

I thought I would see the result 0 1 2, but it gave me 2 2 2 instead.

Then, I changed newData.value=i, in line 3 to newData={value:i}, and the result comes to 0 1 2, as I wanted it to be.

I am confused. What's the difference between those two methods?

JLRishe
  • 99,490
  • 19
  • 131
  • 169
CodyChen
  • 11
  • 2
  • 3
    The difference is that `obj = {value: 1}` is creating a whole new object and overwriting a variable. `obj.value = 1` just overwrites one property on an existing object. – JLRishe Dec 21 '14 at 15:47
  • The function just uses one property 'value' of this object. Couldn't I just overwrite this property instead of the whole object? – CodyChen Dec 21 '14 at 16:50
  • Yes, there are certainly ways to do what you are doing without replacing the whole object. I think it's a good question and not really a duplicate of the question linked above. I and 3 other people have voted to reopen this question, so hopefully one more will come along soon. – JLRishe Dec 21 '14 at 16:56

1 Answers1

1

The reason this is happening is that when you pass newData to foo(), you are passing a reference to that object. Then when you use setTimeout with an anonymous function, that same object is "captured" inside that anonymous function (this is known as a "closure").

Your for loop continues, and the object's value property changes, so when the functions that you passed to setTimeout are ultimately called, the object's value property is now 2, and that is what is displayed.

In your case, there is a simple solution. Just assign data.value's current value to a variable inside your foo function and use that when you call setTimeout:

function foo(data) {
    var val = data.value;  // copy the value to a local variable

    setTimeout(function(){
        console.log(val);
    },1000)
}

The reason the code works as expected when you use {value: i} is that it results in you passing a whole new object to foo every time, and that object is never modified after the fact. There are indeed some benefits to doing it this way, but there are also times when you only want to modify one property and not create a whole new copy. The above will allow you to do that.

The behavior of variables inside closures is an issue that comes up a lot in JavaScript development. See this for more discussion on the topic: JavaScript closure inside loops – simple practical example

Community
  • 1
  • 1
JLRishe
  • 99,490
  • 19
  • 131
  • 169