Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trial
Laurence Foley
16,695 PointsProblem with using the " this " keyword
If I create an object and add a few methods that use the " this " keyword, and then call those methods after a click event, the context of the " this " will change from the object to the html element.
Example:
var bob = {
first_name: "Bob",
last_name: "Jones",
fullName: function() {
return this.first_name + " " + this.last_name;
},
printName: function() {
console.log(this.fullName());
}
}
// This method will print out bob's full name to the console
bob.printName();
// This returns an error as the 'this' keyword will now refer to
// the dom element and not the bob object
document.getElementById('btn').onclick = bob.printName;
What is this best way to deal with this problem so that the " this " keyword all ways refers to the object and not the html element, or is the a better way to go about writing methods that you intend to call after a click event?
Thanks.
3 Answers
geoffrey
28,736 PointsThis is an interesting question for me as I'm reviewing completly Javascript and all Its weird parts at the moment. I found the answer to your question.
As you typed in the example you give, that works when you call the method with function outside your object, this works because when you click on the button the click events is in the global scope and thus looks for a function to call in this global scope
So if you want to leave the method inside your object and call and still print the name as it should you can do that:
var bob = {
first_name: "Bob",
last_name: "Jones",
fullName: function() {
console.log(this.first_name + " " + this.last_name);
return this.first_name + " " + this.last_name;
},
printName: function() {
var printFullName = function(){
bob.fullName();
};
printFullName();
}
}
bob.printName();
document.getElementById('btn').onclick = bob.printName;
You can test it within this js fiddle;
So as you can see I set a function expression inside my printName method which calls the fullName method on Bob. It might looks weird, but if this works, It's because the function expression printFullName gets attached to the global scope, that's why your onclick event can access that function unlike the methods available within the bob object.
You could as well directly write your printName function outside the bob object, this way, this function will be directly in the global scope, and the function will be called as well when you click the button.
In fact, that sounds really odd, but that's the way Javascript works and that can cause some issues whith our understanding.
If you want further explainations check the example above:
var c = {
name: 'the c object',
log: function(){
this.name = 'the name of c object has changed !';
console.log(this.name);
}
}
console.log("first call c.name :", c.name);
//second call I use this time the log method to modify the name property, this work, I can easily modify the name with the this keywords which is bound to the 'c' object.
console.log(c.log());
But what If we want a function expression inside our method that modifies the name property of our project, as we did with your example to have something working with the clicked button ?
Method directly called (this works obviously);
var c = {
name: 'the c object',
log: function(){
this.name = 'the name of c object has changed !';
console.log(this.name);
}
}
console.log("first call c.name :", c.name);
//second call I use this time the log method to modify the name property, this work, I can easily modify the name with the this keywords which is bound to the 'c' object.
console.log(c.log());
Method with function expression inside the method (this won't work');
var c = {
name: 'the c object',
log: function(){
this.name = 'the name of c object has changed !';
var modifyName = function(){
this.name = "I change the name for the second time";
};
modifyName();
console.log(this);
}
}
console.log("first call c.name :", c.name);
//second call I use this time the log method to modify the name property
console.log(c.log());
If you check the console log, you'll see that the name of the 'c' object is still the last modified name => "the name of c object has changed !".
Why ? Because as you can see the modifyName function expression has a this keyword, but this isn't bound to the c object, It's bound to the global scope... that's the way Javascript works under the hood.
One common pattern to solve this issue is to use a reference for the this keyword.
This way:
var c = {
name: 'the c object',
log: function(){
var self = this;
self.name = 'the name of c object has changed !';
var modifyName= function(){
self.name = "I change the name for the second time";
};
modifyName();
console.log(self);
}
}
console.log("first call c.name :", c.name);
//second call I use this time the log method to modify the name property
console.log(c.log());
That time, you will see as output the object but with the name changed ! The name that have been changed thanks the function expression within the log method ! Because thanks the variable self we set, we keep a reference with the this keyword, our actual object and we thus are able to modify its properties....
I hope my explaination makes sense and help a bit. Maybe some people will be able to give better explaination.
Jose Lupianez
4,653 PointsA simple fix would be by changing console.log(this.fullName()); to console.log(bob.fullName());. The problem is that you are binding "this" to global scope as the reference to the function in this case is the onclick which is on the global scope. It works perfectly on bob.printName(); because the reference to the function is the object. You could also force it to bind to object by using bind().
This should work too:
var bob = {
first_name: "Bob",
last_name: "Jones",
fullName: function() {
return this.first_name + " " + this.last_name;
},
printName: function() {
console.log(this.fullName());
}
};
// This method will print out bob's full name to the console
bob.printName();
// This returns an error as the 'this' keyword will now refer to
// the dom element and not the bob object
document.getElementById('btn').onclick = bob.printName.bind( bob );
geoffrey
28,736 PointsYes nice way to achieve it. +1
subin walter
5,189 Pointsthis will refer to which object it is called by in this case you are passing it as an handler to onclick event so this will point to the button object which calls the handler.If u replace this with bob (name of your object) in your code u will get the desired result.(e.g. bob.first_name,bob.last_name,bob.fullName) instead of this.