The more confusing part is actually about the 3rd example, but let me answer your specific question about the second example:
The question is why (object.getName)();
will print "My object"
instead of "The window"
like the third?
Think of how JavaScript resolves the pieces of your code:
object // This is the reference to the object you've made. No surprises here
object.getName // A reference to the function `getName`, with `this` bound to `object`
(object.getName) // This is the same thing as #2. The parens aren't important here
object.getName(); // Invoking the function, as you normally would
(object.getName)(); // Strange syntax, but just invoking the function all the same
But what about when "The window" gets printed?
This is the more confusing part.
(object.getName = object.getName)(); // Things start to go strange here
This is partly because of the assignment (=
) operator, and it is because JavaScript function binding (this keyword) is lost after assignment.
When you reference the function object and then assign that reference to anything, it is passed as a "value type" instead of a "reference type". This drops the this
pointer, unless you bind it back manually. See the documentation for the this
keyword, when calling a function as an object method:
When a function is called as a method of an object, its this
is set to the object the method is called on.
What this means may become a little more clear by modifying your example:
var name = "The window";
var object = {
name: "My object",
getName: function() {
console.log(this); // See what `this` is bound to
return this.name;
}
}
object.getName(); // Prints `Object {name: "My object"}`
(object.getName)(); // Also prints `Object {name: "My object"}`
var func = object.getName;
func(); // Prints `Window`
(object.getName = object.getName)(); // Also prints `Window`
You'll notice the same behavior if you try to pass the function as a callback to some other function:
function test(callback) {
callback();
}
test(object.getName); // Prints `Window`
However, if the base object is still bound while you are calling it, then you will see the normal object behavior:
function test(obj) {
obj.getName();
}
test(object); // Prints `Object {name: "My object"}`
Other interesting or useful behavior
Doing the assignment doesn't break the original object. Even if you've gotten this
mangled on some other reference, object
is still intact:
(object.getName = object.getName)(); // Prints `Window`
object.getName(); // Prints `Object {name: "My object"}`. It didn't break
If you assign the function reference back to a different property on the object, you'll see that this
gets assigned correctly on the new property. This is because the important part is whether you are specifying the base object while you are calling the function:
var temp = object.getName;
temp(); // Prints `Window`
object.somethingElse = temp;
object.somethingElse(); // Prints `Object {name: "My object"}`. `this` is correctly bound
If you pull a function off an object and stick it on a totally different object then it will still work, but this
will be bound to the new object.
Object prototypes rely on this behavior in order to do their magic.
var basketball = {
name: "Sports equipment?!",
}
basketball.doStuff = object.getName;
basketball.doStuff(); // Prints `Object {name: "Sports equipment?!"}`
If for some reason you get a reference to your function without this
being bound, you can still correct it, as long as you have a reference to the original object:
var temp = object.getName;
temp(); // Prints `Window`
var temp2 = temp.bind(object);
temp2(); // Prints `Object {name: "My object"}` because you bound `this`
temp.apply(object); // Also prints `Object {name: "My object"}`