Getting to the bottom of the this Variable

In PHP, when we call a function like updateTotalWeightLifted():

126 lines app/Resources/views/lift/index.html.twig
... lines 1 - 64
{% block javascripts %}
... lines 66 - 67
<script>
var RepLogApp = {
... lines 70 - 91
handleRepLogDelete: function(e) {
... lines 93 - 102
$.ajax({
... lines 104 - 105
success: function() {
$row.fadeOut('normal', function() {
... line 108
RepLogApp.updateTotalWeightLifted();
});
}
});
},
... lines 114 - 117
};
... lines 119 - 123
</script>
{% endblock %}

We expect the this variable inside of that function to be whatever object we're inside of right now. In that case, it is. But in so many other cases, this is something different! Like inside handleRowClick and handleRepLogDelete:

126 lines app/Resources/views/lift/index.html.twig
... lines 1 - 64
{% block javascripts %}
... lines 66 - 67
<script>
var RepLogApp = {
initialize: function($wrapper) {
... lines 71 - 72
this.$wrapper.find('.js-delete-rep-log').on(
... line 74
this.handleRepLogDelete
);
this.$wrapper.find('tbody tr').on(
... line 78
this.handleRowClick
);
},
... lines 82 - 117
};
... lines 119 - 123
</script>
{% endblock %}

What's going on? And more importantly, how can we fix it? When I'm inside a method in an object, I want this to act normal: I want it to point to my object.

How do I Know what this Is?

Here's the deal: when you call a function in JavaScript, you can choose to change what this is inside of that function when you call it. That means you could have one function and 10 different people could call your function and decide to set this to 10 different things.

Now, in reality, it's not that bad. But we do need to remember one rule of thumb: whenever you have a callback function - meaning someone else is calling a function after something happens - this will have changed. We've already seen this a lot: in the click functions, inside of .each(), inside of success and even inside of $row.fadeOut():

126 lines app/Resources/views/lift/index.html.twig
... lines 1 - 64
{% block javascripts %}
... lines 66 - 67
<script>
var RepLogApp = {
initialize: function($wrapper) {
... lines 71 - 72
this.$wrapper.find('.js-delete-rep-log').on(
... line 74
this.handleRepLogDelete
);
this.$wrapper.find('tbody tr').on(
... line 78
this.handleRowClick
);
},
updateTotalWeightLifted: function() {
... line 84
this.$wrapper.find('tbody tr').each(function() {
totalWeight += $(this).data('weight');
});
... lines 88 - 89
},
handleRepLogDelete: function(e) {
... lines 93 - 102
$.ajax({
... lines 104 - 105
success: function() {
$row.fadeOut('normal', function() {
... line 108
RepLogApp.updateTotalWeightLifted();
});
}
});
... lines 113 - 117
};
... lines 119 - 123
</script>
{% endblock %}

So what is this inside of these functions? It depends on the situation, so you need to read the docs for the success function, the fadeOut() function or the .each() function to be sure. For fadeOut(), this ends up being the DOM Element that just finished fading out. So, we can actually call $(this).remove():

126 lines app/Resources/views/lift/index.html.twig
... lines 1 - 64
{% block javascripts %}
... lines 66 - 67
<script>
var RepLogApp = {
... lines 70 - 91
handleRepLogDelete: function(e) {
... lines 93 - 102
$.ajax({
... lines 104 - 105
success: function() {
$row.fadeOut('normal', function() {
$(this).remove();
... line 109
});
}
});
},
... lines 114 - 117
};
... lines 119 - 123
</script>
{% endblock %}

That's the same as before.

Being a Magician with this!

Let's have a little fun with the weirdness of this. Create a new function - just for debugging - called whatIsThis with a single argument, a greeting. Inside, just console.log() this and our greeting:

132 lines app/Resources/views/lift/index.html.twig
... lines 1 - 64
{% block javascripts %}
... lines 66 - 67
<script>
var RepLogApp = {
... lines 70 - 84
whatIsThis: function(greeting) {
console.log(this, greeting);
},
... lines 88 - 123
};
... lines 125 - 129
</script>
{% endblock %}

Next, at the bottom of initialize, add this.whatIsThis() and pass it hello:

132 lines app/Resources/views/lift/index.html.twig
... lines 1 - 64
{% block javascripts %}
... lines 66 - 67
<script>
var RepLogApp = {
initialize: function($wrapper) {
... lines 71 - 81
this.whatIsThis('hello');
},
... lines 84 - 123
};
... lines 125 - 129
</script>
{% endblock %}

Simple enough! And since we're calling this function directly - not as a callback - I would expect this to actually be what we expect: our RepLogApp object. Let's find out. Refresh! Expand the logged object. Yea, it's RepLogApp! Cool!

But now, let's get tricky! Create a new variable called newThis and set it to an object with important stuff like cat set to meow and dog set to woof:

133 lines app/Resources/views/lift/index.html.twig
... lines 1 - 64
{% block javascripts %}
... lines 66 - 67
<script>
var RepLogApp = {
initialize: function($wrapper) {
... lines 71 - 81
var newThis = {cat: 'meow', dog: 'woof'};
... line 83
},
... lines 85 - 124
};
... lines 126 - 130
</script>
{% endblock %}

To force newThis to be this inside our function, call the function indirectly with this.whatIsThis.call() and pass it newThis and the greeting, hello:

133 lines app/Resources/views/lift/index.html.twig
... lines 1 - 64
{% block javascripts %}
... lines 66 - 67
<script>
var RepLogApp = {
initialize: function($wrapper) {
... lines 71 - 81
var newThis = {cat: 'meow', dog: 'woof'};
this.whatIsThis.call(newThis, 'hello');
},
... lines 85 - 124
};
... lines 126 - 130
</script>
{% endblock %}

Oh, and quick note: this.whatIsThis is, obviously, a function. But in JavaScript, functions are actually objects themselves! And there are a number of different methods that you can call on them, like .call(). The first argument to call() is the variable that should be used for this, followed by any arguments that should be passed to the function itself.

Refresh now and check this out! this is now our thoughtful cat, meow, dog, woof object. That is what is happening behind the scenes with your callback functions.

Now that we understand the magic behind this, how can we fix it? How can we guarantee that this is always our RepLogApp object when we're inside of it?

Leave a comment!

  • 2017-09-21 Victor Bocharsky

    Hey Vesela,

    Downloads are available for subscribers or course owners only - you'll see a "Download" -> "Course Code" buttons in the right top corner of every chapter page. If you're not a subscriber or owner of this course - you can look over the repository of this course which is public: https://github.com/knpunive...

    Cheers!

  • 2017-09-21 Vesela

    Hi how can I download the source ?