Reputation: 325
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!
Upvotes: 0
Views: 151
Reputation: 9597
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:
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.
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.
Upvotes: 2
Reputation: 2257
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.
Upvotes: 1
Reputation: 1268
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.
Upvotes: 0