Reputation: 3255
When initially invoking a function, the first this
within the first function that is called refers to the parent object foo
but on a subsequent function called by that first function this
refers to the window object?
var foo = (function Obj(){
var t = this;
return {
getThis: getThis,
getSecondThis: getSecondThis
};
function getThis(){
console.log(this);
console.log(t);
getSecondThis()
return false;
}
function getSecondThis(){
console.log(this);
console.log(t);
return false;
}
})();
foo.getThis();
If I change the call from getSecondThis()
to this.getSecondThis()
then the this
within getSecondThis()
refers to the parent object foo
see the code below
var foo = (function Obj(){
var t = this;
return {
getThis: getThis,
getSecondThis: getSecondThis
};
function getThis(){
console.log(this);
console.log(t);
this.getSecondThis() //line edited
return false;
}
function getSecondThis(){
console.log(this);
console.log(t);
return false;
}
})();
foo.getThis();
The getSecondThis()
is within the scope of the parent object foo
but window is returned when this
is not specified on the second call.
Upvotes: 1
Views: 51
Reputation: 76405
It's just the way JS binds the calling context: JS binds the context (the this
reference) ad-hoc. Meaning: depending on how, where and by what means it is invoked, this
will reference a different object.
I've explained this in some detail before here, and in the linked answers found on the bottom
Basically, functions are first class objects, meaning that, like any value, you can assign a function to a multitude of variables/properties. Of course, if you assign a function to an object (as a property), that function is often referred to as a method, and you'd expect this
to point to the object that owns that method.
However, as Pointy noted in the comments: An object cannot own another object. Objects can be referenced by one or more properties of another object.
JS will kindly set this
to refer to the object that owns the function. But if you then assign the object to a variable, it would make no sense to have this
point to that same object. Think of situations where you're passing functions as function arguments (callbacks in jQuery and so on). You probably want this
to reference the new context (certainly the case in jQ event handlers!).
If no context is provided, JS sadly defaults the this
reference to the global object.
You can explicitly bind a function to a given context using the Function.prototype.bind
call.
If you want to specify the context for a single call, you can use Function.prototype.call(context, arg1, arg2);
or Function.prototype.apply(context, [args]);
Most larger projects (toolkits like jQ for example) solve this issue by taking advantage of closure scoping. Using the module pattern, for example, is a common, and easy way to control the context. I've explained this, too, complete with graphs to illustrate what is going on :)
Some examples/puzzles to make this easier to follow or more fun:
var obj = (function()
{//function scope
'use strict';//this will be null, instead of global, omit this and this can be window
var obj = {},
funcOne = function()
{
console.log(this, obj, this === obj);
funcTwo(this);
obj.funcTwo();
obj.funcTwo(this);
obj.funcTwo(obj);
},
funcTwo = function(that)
{
console.log(this, obj, this === obj, this === that, that === obj);
};
obj.funcOne = funcOne;
obj.funcTwo = funcTwo;
return obj;//is assigned to the outer var
}());
obj.funcOne();
//output:
//- first logs itself twice, then true
//then calls made in funcOne:
funcTwo()
console.log(this, obj, this === obj, this === that, that === obj);
//- this: undefined (omit 'use strict' and it'll be window), obj,
// this === obj => false, this === that => false, that === obj => true
obj.funcTwo();
console.log(this, obj, this === obj, this === that, that === obj);
//logs obj twice, because obj.funcTwo means the context === obj
//this === obj is true (of course)
//that is undefined, so this === that and that === obj are false
//lastly
obj.funcTwo(this);
obj.funcTwo(obj);
You should be able to work that out. You know the context in which funcOne
is being executed, and you know what the effects are of invoking funcTwo
as a method of obj
Rule of thumb:
I hesitated to write this, because it's far from accurate, but 8/10 cases. Assuming no code has been meddling with contexts through bind
, call
, and apply
, you can work out the context using this trick:
someObject.someMethod();
/\ ||
|===this===|
//another object:
var obj2 = {
borrowed: someObject.someMethod,
myOwn: function()
{
this.borrowed();
}
};
obj2.myOwn();//this === obj2 (as explained above),
\\
\==> this.borrowed === obj2.borrowed
\\
\==> ~= someObject.someMethod.call(obj2);//function is executed in context of obj2
//simple vars
var alias = someObject.someMethod;//assign to var
alias();//no owner to be seen?
||
?<==|
//non-strict mode:
[window.]alias();
/\ implied ||
|| ||
|==<this>===|
//strict mode
alias.call(undefined);//context is undefined
Upvotes: 2