Question

I'm experiencing difficulty figuring out what 'this' refers to, in multiple instances of the following code:

 jQuery(function ($) {

    var coreChat = {

      fetch: function (fn) {
        $.ajax({
          url: "https://api.parse.com/1/classes/chats",
          data: {
             order: 'createdAt',
             limit: 10
          },
          type: "GET"
        })
        .done(function(data) {
          var messages = [];
          for (var i = 0, len = data.results.length; i < len; i++) {
            messages.push(data.results[i].text);
          }

          return fn(messages);
        });
      },

      send: function (message) {
        var data = JSON.stringify({text: message});
        $.ajax({
          url: "https://api.parse.com/1/classes/chats",
          data: data,
          type: "POST"
        });
      },

      display: function (messages) {

        // http://jsperf.com/update-only-text-vs-remove-dom

        var messageNode = $(".messages").find("li");

        messageNode.each(function (i) {
          var $this = $(this);
          if ($this.text() !== messages[i]) {
            $this.text(messages[i]);
          }
        });

      }

    };


    var myChat = {

      init: function () {
        **this**.fetchInitialData();
        **this**.bindSendEvent();
        **this**.refresh();
      },

      fetchInitialData: function () {
        var messagesWrapper = $(".messages");
        for (var i = 0; i < 10; i++) {
          $("<li></li>").appendTo(messagesWrapper);
        }
        myChat.updateMessages();
      },

      bindSendEvent: function () {
        $(".send").on("click", function (e) {
          var input = $(".draft");
          myChat.send(Chat.username + ": " + input.val());
          input.val("");
          e.preventDefault();
        });
      },

      refresh: function () {
        setInterval(function () {
          myChat.updateMessages();
        }, 3000);
      },

      updateMessages: function () {
        this.fetch(function (messages) {
          myChat.display(messages);
        });
      }

    };

    $.extend(myChat, coreChat);
    myChat.init();

  });

More specifically, in the bottom half of the code - in the myChat object, there is an "init" property which, as a value, contains a function...

var myChat = {

      init: function () {
        this.fetchInitialData();
        this.bindSendEvent();
        this.refresh();
      },

What would 'this' be referring to here? And does the fact that coreChat was extended into myChat on the 2nd to last line of the code make a difference in determining what 'this' refers to?

$.extend(myChat, coreChat);

Also, there's another 'this' in the updateMessages property of the myChat object...

updateMessages: function () {
        this.fetch(function (messages) {
          myChat.display(messages);
        });
      }

^ What does this refer to in this instance? The fetch function from coreChat is being executed on 'this', but again, what is 'this'?

And as a quick tangential question - in the updateMessages function, it calls the 'fetch' function.'fetch' is asynchronous with a callback function ('fetch' is originally found in the coreChat object). But then in updateMessages, what is that function that is passed in as a parameter to fetch? Is fetch supposed to execute it's original callback function, and then this parameter is to act as the parameter of the callback? I'm definitely confused there.

^ I understand that's 2 separate questions - I'll delete it if that poses an issue, and post it in a new separate question, but I just thought, since someone may have already gone thru the entire code, maybe he'd/she'd already be equipped to answer the 2nd question and I might as well ask...

THANKS IN Advance!

Was it helpful?

Solution 2

In your code, "this" is pretty much always referring to the myChat object.

If you were to call init you would write myChat.init(). init is part of the myChat object, so this refers to the containing object. Init is specified within the scope of myChat, as are all of your other functions, meaning that this function and all others are part of myChat, and therefore this is myChat

var myChat = {

      init: function () {
        this.fetchInitialData();
        this.bindSendEvent();
        this.refresh();
      },
      ...
}

Also, if you look at the jquery documentation on extend (http://api.jquery.com/jQuery.extend/), the description is "Merge the contents of two or more objects together into the first object.". So when you $.extend(myChat, coreChat);, what you're actually doing is copying all properties from coreChat into the myChat object. So, when you call fetch in this code, you're actually calling the fetch method that has been copied into your myChat object, so this also still refers to myChat. To further reinforce that these methods were added to the myChat object, notice you're calling myChat.display? That was defined in coreChat, but you're accessing it using myChat instead.

updateMessages: function () {
        this.fetch(function (messages) {
          myChat.display(messages);
        });
      }

There is an instance where this does not refer to myChat. That is in the display function of coreChat. Since you're using the jquery .each function, within the scope of that function, this refers to the list item that the function is being run on, and NOT the coreChat object (or more precisely the myChat copy of this coreChat function as explained above).

var messageNode = $(".messages").find("li");
messageNode.each(function (i) {
          var $this = $(this);
          if ($this.text() !== messages[i]) {
            $this.text(messages[i]);
          }
        });

As for part 2 of your question:

The function that is passed as a parameter to fetch is a callback function for when fetch is completed. The definition for fetch includes a function as a parameter:

fetch: function (fn) {   

In the .done function, the callback function is invoked, passing in the message data, and returning the result of that callback:

return fn(messages);

So, you call .fetch and pass in your function, fetch executes and does all of its processing, and then fetch calls your passed-in function.

OTHER TIPS

If you are coming from an object-oriented world such as Java, 'this' is a bit of a different concept. In languages, like Java, 'this' refers to the currently executing object and is defined by how the method is declared. But, in Javascript, 'this' refers to how the function was invoked and is really the execution context of the function. In fact, to quote John Resig in 'Secrets of the Javascript Ninja', it should really be called the invocation context.

To answer your questions:

  1. this in the init function is referring to the myChat object. No, the fact that you used jQuery.extend to merge the two objects has no bearing on the value of this. That is a bit misleading on jQuery's part.

  2. this in updateMessages also refers to the myChat object

One thing to be wary of in your above code is the setInterval portion in refresh. Your code is correct since it invokes the function on the myChat object explicitly, but in that function, this refers to the global object (most likely 'window' if you're running in a browser).

Finally, to answer your third question regarding the callback, fetch runs an asynchronous POST. When that POST completes, the done callback is called. The value returned from that done callback is the value of invoking the function passed to fetch. In your example, that looks to be undefined since your function calls display but display doesn't specify a return value. Any function that doesn't specify a return will return undefined by default.

Hope that helps.

this in JavaScript depends on how the function itself is used. It changes depending on the parent object, bind/apply/call invocations, if it's used with a constructor, etc.. check out this on MDN for all the gory details.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top