'"this" in javascript'

by Tammo — 3 minutes

In object-oriented languages, like Java, this refers to the instance of the class where you run the method. In javascript this is often also the case, but not always. In this post we'll explore some situations. And I give some tips on how to deal with them. The normal case. The function eat is defined on carrot. And we simply run it. this will refer to the enclosing object, which is carrot, with name "carrot".

var carrot = {
  name: "carrot",
  eat: function () {
    console.log(this);
    console.log("eating " + this.name);
  },
};

carrot.eat(); //result: eating carrot

When we define and run eat on the toplevel, this will be the window object, with name "result".

function eat() {
  console.log(this);
  console.log("eating " + this.name);
}

eat(); //eating result

With the function apply, we can change where the function will run on. This will effectively change the meaning of this.

var eggplant = {
  name: "eggplant",
};

carrot.eat.apply(carrot); //eating carrot
carrot.eat.apply(eggplant); //eating eggplant
eat.apply(carrot); //eating carrot
eat.apply(eggplant); //eating eggplant

With bind, we can bind a function to another object. This will create a new function that you'll have to assign to a new variable. The old function stays the same. Notice that you cannot override that with apply.

var eatCarrot = eat.bind(carrot);
eatCarrot(); //eating carrot
eatCarrot.apply(eggplant); //eating carrot
var eatEggplant = carrot.eat.bind(eggplant);
eatEggplant(); //eating eggplant
carrot.eat(); //eating carrot

With callback functions, we don't even need to explicitly use apply to mess things up.

var tammo = {
  name: "tammo",
  feed: function (callbackFunction) {
    return callbackFunction();
  },
  feedSelf: function (callbackFunction) {
    return callbackFunction.apply(this);
  },
  feedParameter: function (parameter, callbackFunction) {
    return callbackFunction.apply(parameter);
  },
};

tammo.feed(eat); //eating result
tammo.feedSelf(eat); //eating tammo
tammo.feedParameter(carrot, eat); //eating carrot
tammo.feedParameter(eggplant, function () {
  //eggplant
  console.log(this);
});

When we use anonymous callback functions inside an object, we have to remember that this does not refer to the object anymore. If we want to use it, we'll have to assign it to an intermediate variable.

console.log("this inside anonymous functions");
var cook = {
  name: "cook",
  cook: function () {
    //here this is the cook (when we call it normally)
    var cook = this; //We have to save "this" to another variable to use it within the anonymous function
    tammo.feedParameter(carrot, function () {
      //here this will be the eggplant
      console.log(
        cook.name + " is cooking a " + this.name + " for " + tammo.name,
      );
    });
  },
  cook2: function () {
    var cook = this;
    tammo.feed(function () {
      //here this will be window (with name "result" in jsfiddle)
      console.log(
        cook.name + " is cooking a " + this.name + " for " + tammo.name,
      );
    });
  },
};

cook.cook(); //cook is cooking a carrot for tammo
cook.cook2(); //cook is cooking a result for tammo
var restaurant = {
  name: "restaurant",
  go: function () {
    cook.cook2();
  },
};
restaurant.go(); //cook is cooking a result for tammo

Using this knowledge we can create DSL-like functions with funky usage of this.

var for12 = function (callback) {
  callback.apply(1);
  callback.apply(2);
};
for12(function () {
  console.log("number " + this);
});

But it's clearer to just use parameters.

var for34 = function (callback) {
  callback(3);
  callback(4);
};
for34(function (number) {
  console.log("number " + number);
});

And that's basically the gist of it. You can do lots of funky things with this. But your colleagues (and future you), will be happier if you don't. Some frameworks with callbacks may use it though, so be alert. The code also exists on jsfiddle, where you can easily try it out.

meerdivotion

Cases

Blogs

Event