Reputation: 51
In the book "Professional JavaScript for Web Developers" 3rd edition, it said that:
var name = "The window";
var object = {
name: "My object",
getName: function() {
return this.name;
}
}
object.getName(); //"My object"
(object.getName)(); //"My object"
(object.getName = object.getName)(); //"The window"
The question is why (object.getName)();
will print "My object"
instead of "The window"
like the third?
I think object.getName
shall return a pointer to the function, just like the third one. The this
shall point to the global object, which is window
in web browser. Therefore it shall print the "The window"
. However it seems that object.getName
returns object.getName
itself. Why?
Upvotes: 4
Views: 189
Reputation: 59111
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
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"}`
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"}`
Upvotes: 2
Reputation: 144689
The result of the assignment operator (=
) is the set value. Consider this example:
> var num;
> num = 3;
< 3 // the returned value of the assignment
The expression returns the function itself after reassigning it to the object. As the this
value is determined by how a function is called the this
value in that case refers the to global object. In the second snippet ((object.getName)();
) the function is called as a member of the object
object and this is why the this
refers to the object
.
Upvotes: 2