Reputation: 10741
Consider the following code:
<script>
var i = 0;
function test() {
var _this = this;
// foo and _this.foo are set to the same number
var foo = _this.foo = i++;
function wtf() {
console.log(_this.foo, foo);
}
$("#thediv").click(wtf);
};
test();
test();
test();
</script>
It seems that console.log(_this.foo, foo) should always output equal numbers (i).
But clicking the div outputs 3 lines (for each console.log call):
2 0
2 1
2 2
It seems that _this.foo
always refers to last this.foo
. Why it is so?
Upvotes: 2
Views: 389
Reputation: 186103
So, this is how it works:
The test
function is invoked three times. Each time a different wtf
function object is created and bound to the DIV as its click handler. This means that after your above code is executed, there will be three click handlers bound to the DIV. When you then click on the DIV, those three handlers are invoked in sequence.
This line
var _this = this;
merely stores the global object into the local variable _this
. If the function test
were to be invoked in an strict-mode environment, this
would be undefined
and the code would throw an error. However, since it's not strict mode, the this
value refers to the global object.
Btw, the i
variable is declared in global code which makes it a global variable (global property).
This line
var foo = _this.foo = i++;
assigns the current value of i
both to the local variable foo
and to _this.foo
. And since _this
is a reference to the global object, the property foo
is a global property (just like i
).
Now, since we invoked the test
function three times, there are also three separate foo
local variables. Each of these variables captures the value of the i
variable in the moment the test
function was invoked. So, the values of those three foo
variables are 0
, 1
, and 2
. Those variables are captured by the three wtf
functions (respectively) through closure. (The first wtf
function captures the first foo
variable, and so on.)
However, unlike the foo
variables, there is only one foo
global property. So, after each invocation of the test
function, the foo
global property is incremented. As a result, after test
has been invoked three times, the value of _this.foo
is 2
. (This is before the DIV was clicked.)
Now, when the DIV is clicked, the three wtf
functions will execute. Each of those functions will print the value of _this.foo
which is 2
. However, each of those wtf
functions captured a different foo
variable through closure. The values of those three foo
variables are 0
, 1
, and 2
, respectively.
Upvotes: 1
Reputation: 1319
Its a tricky one, :)
The first and foremost thing that you have to understand is, you are calling the test
function without a new
prefix, which makes the this
pointer inside the function refer to the window
object
<script>
var i = 0;
function test() {
var _this = this; //** this referes to the window object
var foo = _this.foo = i++; //** incrementing the global var and assigning that to a local var and a window.foo var
function wtf() {
console.log(_this.foo, foo); //** reads window.foo and its local var foo
}
$("#thediv").click(wtf); //** creates a new lister every time the test function gets called
};
//** calling without the new keyword
test(); //** creates foo-1, and wft-1 in memory, assigns foo-1=0; window.foo=0
test(); //** creates foo-2, and wft-2 in memory, assigns foo-2=1; window.foo=1
test(); //** creates foo-3, and wft-3 in memory, assigns foo-3=2; window.foo=2
</script>
(plz read the commented part inside the code)
Now, when you click on the div
, which practically has 3 functions listening to its click event (3 inline wtf
functions inside the test
function)(each call to the test
function creates a new wtf
inline function). Each of these inline functions reads its on local var foo
each of then have values 1,2,3
respectively.
Upvotes: 1
Reputation: 4257
When test() is run, this
is a reference to window
for each of your three test() calls, so you are actually updating window.foo
when you assign to _this.foo
and referencing window.foo
in your console.log
.
Then, when wtf()
is invoked, the _this
variables in each of the wtf()
closures are the same - they all point to window
See this jsfiddle: http://jsfiddle.net/8cyHm/
I added some additional parameters to console.log to show what is happening
Upvotes: 2
Reputation: 10763
In your function wtf
, the value captured in the variable foo
is a scalar - the actual numeric value. Since it's capturing the scalar value at the moment the function is created, that function is closing over the value 0, 1, 2, etc. That's why it seems to increment.
However, when _this is captured, it's captured as a reference to a single instance which has a field named foo. Therefore, with _this.foo, you are referring to a field on the same instance.
So to sum it up, value types like integers are captured by their value, whereas an object is captured by it's reference.
Upvotes: 3
Reputation: 16063
If you are testing in Chrome you may well be hitting a bug in console.log. See : Javascript Funky array mishap
Try changing to :
console.log(_this.foo + ' = ' + foo);
Upvotes: -2