Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Getting to the bottom of the this Variable

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

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

... 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:

... 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():

... 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():

... 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:

... 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:

... 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:

... 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:

... 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!

5
Login or Register to join the conversation
Laurens M. Avatar
Laurens M. Avatar Laurens M. | posted 2 years ago

Why not just use RepLogApp instead of struggling with "this"? Nevermind it's explained on the next page. :-)

Reply

Hey Laurens M.

And that is awesome! We are very happy that next chapter answered your question!

Cheers and happy learning!

Reply
Laurens M. Avatar

Thank you Vladimir you guys are doing a great job! Bests

Reply
Default user avatar

Hi how can I download the source ?

Reply

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!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial uses an older version of Symfony... but since it's a JavaScript tutorial, the concepts are still ? valid!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.0",
        "symfony/symfony": "3.1.*", // v3.1.10
        "twig/twig": "2.10.*", // v2.10.0
        "doctrine/orm": "^2.5", // v2.7.1
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.2
        "symfony/swiftmailer-bundle": "^2.3", // v2.4.0
        "symfony/monolog-bundle": "^2.8", // 2.12.0
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "friendsofsymfony/user-bundle": "~2.0@dev", // dev-master
        "doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.1
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.1
        "symfony/phpunit-bridge": "^3.0" // v3.1.6
    }
}
userVoice