Wenqi Ma
Wenqi Ma

Reputation: 51

Why does `(object.fun = object.fun)() use `window` but (object.fun)() uses `object` for `this`?

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

Answers (2)

Merlyn Morgan-Graham
Merlyn Morgan-Graham

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

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"}`

Upvotes: 2

Ram
Ram

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

Related Questions