Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange value for the "this" object

Regarding this code:

var name = "Jaguar";
var car = {
  name:"Ferrari",
  getName:function(){
    return this.name;
  }
};

alert((car.getName = car.getName)());

The output is: Jaguar.

Why does this object correspond to Window and not the object contained in the car variable?

It seems that the fact to reassign the object's function to itself leads to lose the assignment of this to the object when the function is called...

I'm trying to guess: Does it exist a kind of mechanism (using variable or other) that keep an eye on the non-reassignement of an object's function so that if that situation happens, this mechanism would prevent the assignement of the this keyword as usual (as being equals to the object)?

like image 764
Mik378 Avatar asked Dec 07 '22 11:12

Mik378


2 Answers

The reason is fairly subtle: this in JavaScript is determined entirely by how a function is called. To have this set to car during the call to getName, you have to call getName immediately upon retrieving it from the car object, like this:

car.getName() // or
car["getName"]()

(Or via Function#call or Function#apply, which let you specify the value for this explicitly.)

What you're doing in your example is effectively this:

// Set f to the result of the assignment expression,
// which is a reference to the getName function
var f = (car.getName = car.getName);

// Call it (separately)
f();

...which is different. Functions called in that way get this set to the global object (window, in browsers). (Except in strict mode; in strict mode this would be undefined.)

More (from my anemic blog):

  • Mythical methods
  • You must remember this

Does it exist a kind of mechanism (using variable or other) that keep an eye on the non-reassignement of an object's function so that if that situation happens, this mechanism would prevent the assignement of the this keyword as usual (as being equals to the object)?

I'm not entirely sure I follow that question, but if you want to have a function that always has a preset this value, then yes, there are a couple of ways to do that.

One is to use the new ES5 function bind:

var name = "Jaguar";
var car = {
  name: "Ferrari"
};
car.getName = function(){
  return this.name;
}.bind(car);
alert((car.getName = car.getName)()); // "Ferrari"

bind returns a function that always has this set to the argument you give it.

The other way is to use a closure. And in fact, you can create a bind-like function in ES3 very easily:

function pseudoBind(func, thisArg) {
    return function() {
        return func.apply(thisArg, arguments);
    };
}

That doesn't do everything bind does, but it does the this part. Then you'd have:

var name = "Jaguar";
var car = {
  name: "Ferrari"
};
car.getName = pseudoBind(function(){
  return this.name;
}, car);
alert((car.getName = car.getName)()); // "Ferrari"

More on closures (again from the blog):

  • Closures are not complicated

In a future spec, we'll be getting a declarative way of creating functions that have a preset this value (so-called "arrow functions" because the syntax for them involves uses => rather than the function keyword).

like image 145
T.J. Crowder Avatar answered Dec 27 '22 16:12

T.J. Crowder


Aaah, this resolution...Lets take a gander.

var toy = {
    log : function () { console.log(this); }
};
toy.log() //logs the toy object

So far, it seems like this resolution is static: Wherever the method was defined in, that's its this value.

But! What if we do this:

var sneakyBastard = toy.log;
sneakyBastard(); //logs Window

this is bound to the object it's called with. In the case of toy.log, you called log in the context of the toy object. But, sneakyBastard has no set context, so it's as if you've called window.sneakyBastard.

Now let's take a goose (goose? gander? no...) at your expression:

(car.getName = car.getName)()

...and what does an assignment return? The assigned value, in this case, a function, car.getName. We can break this expression into two:

var returnedValue = (car.getName = car.getName);
returnedValue();

...and from here it's obvious.

like image 34
Zirak Avatar answered Dec 27 '22 16:12

Zirak