0

I have this class in Javascript:

return function (settings) {
    var items = [],
        formHelper = new FormHelper(items),
        that = this;

    this.currentItem;

    this.initialize = function () {
        settings.listFn().then(function (response) {
            angular.copy(response, items);

            if (response.length > 0) {
                that.setCurrentItem(response[0]);
            }
        });
    }
}

In the initialize method, I use a promise. When this promised is finished, the anonymous function is executed. Within the function you see the use of that, which is declared at line 4. When I use the this keyword instead of that, this points to window, while that points to the object.

I am wondering why this is. Could someone explain how this works? Why it behaves like this?

Martijn
  • 22,949
  • 59
  • 161
  • 247
  • It's explained extensively in the MDN documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this – Felix Kling Jan 22 '14 at 08:46
  • also have a look at [How to access the correct `this` / context inside a callback?](http://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-context-inside-a-callback) – Felix Kling Jan 22 '14 at 08:47
  • `this` depends on how the function gets called. `.then` is not aware of your class, that's why you need to keep a reference. – elclanrs Jan 22 '14 at 08:48

2 Answers2

1

The value of this is determined by how the function is called, not by how or where it is defined.

Since you are passing the function to some third party code (.then), you have to be aware that it is not you who calls the function, but the third party code, and you won't have any control over how the function is called.
At some point the callback will most likely be called in the way that most functions are called, simply as

someFunction()

In that case, this refers to the global object which happens to be window in browsers (this is undefined if the function is in strict mode).

More information on MDN.

Felix Kling
  • 705,106
  • 160
  • 1,004
  • 1,072
0

The callback you pass to the promise gets called asynchronously. In that case, this refers to the default context, which is the global window in the browser, and module in node.js, for example.

If you want to prevent this, use .bind() to set the context of the function to your liking. (Note: Without polyfill, .bind() is not available until IE9)

this.initialize = function () {
    settings.listFn().then(function (response) {
        angular.copy(response, items);

        if (response.length > 0) {
            that.setCurrentItem(response[0]);
        }
    }.bind(this));
}
MildlySerious
  • 7,578
  • 2
  • 24
  • 29
  • *"[...] gets called asynchronously. In that case, `this` refers to the default context, [...]"* The fact that `this` refers to `window` has nothing to do with the function being called asynchronously. You make it sound like it does. – Felix Kling Jan 22 '14 at 09:24
  • Does `this` in an asynchronous call always refers to the `window`? – Martijn Jan 22 '14 at 09:25
  • @FelixKling What is the fact why `this` refers to `window`? – Martijn Jan 22 '14 at 09:26
  • @Martijn: Because the function will (most likely!) be called "normally", like `func();`. And in that case, `this` always refers to `window` (and `undefined` in strict mode). See the MDN documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#Simple_call. – Felix Kling Jan 22 '14 at 09:28
  • @FelixKling But the function is called within my class, ain't that strange behavior? – Martijn Jan 22 '14 at 09:29
  • @Martijn: No, the function is not called in your class. It is called by `.then`. You are only defining a function and pass it to another function so that that function can call it. And as you hopefully have already read in the MDN documentation, the value of `this` is determined by how the function is **called** not by how or where it is **defined**. – Felix Kling Jan 22 '14 at 09:30
  • @FelixKling, I think it's very confusing, but thanks. Could you create an answer so I can accept it? (and yes, I just read it :)) – Martijn Jan 22 '14 at 09:33