The window Object & Global Variables

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

Now that we're using this fancy self-executing function, we don't have access to RepLogApp anymore:

(function() {
var RepLogApp = {
... lines 3 - 50
};
... lines 52 - 69
})();

How can we fix that? Very simple. Instead of var RepLogApp, say window.RepLogApp:

(function() {
window.RepLogApp = {
... lines 3 - 50
};
... lines 52 - 69
})();

Back in the template, I'll delete the console.log() for Helper:

... lines 1 - 64
{% block javascripts %}
... lines 66 - 69
<script>
console.log(Helper);
... lines 72 - 75
</script>
{% endblock %}

And then go back and refresh. It works! No error in the console, and delete does its job!

What is this window?

So what the heck just happened? Here's the deal: when you're executing JavaScript in a browser - which for you is probably always - you always have access to a global window variable. In fact, it's even more important than that. This window variable holds all of the global variables. What I mean is: if you set a key on the window object, like RepLogApp, this becomes a global variable. That means you can reference RepLogApp from anywhere else, and this is actually referencing window.RepLogApp. More on that in a second.

Passing Yourself Global Variables

Inside of our self-executing function, we - of course - also have access to any global variables, like window or the $ jQuery variable. But, instead of relying on these global variables, you'll often see people pass those variables into the function. It's a little weird, so let's see it.

Right now, inside of our self-executing function, we're using two global variables: window and $, for $.ajax, for example:

(function() {
window.RepLogApp = {
... lines 3 - 21
handleRepLogDelete: function (e) {
... lines 23 - 35
$.ajax({
... lines 37 - 44
});
},
... lines 47 - 50
};
... lines 52 - 69
})();

At the bottom of the file, between the parentheses, reference the global window and jQuery variables and pass them as arguments to our function. On top, add those arguments: window and $:

(function(window, $) {
window.RepLogApp = {
... lines 3 - 69
})(window, jQuery);

Now, when we reference window and $ in our code, we're no longer referencing the global objects directly, we're referencing those arguments.

Why the heck would you do this? There are two reasons, and neither are huge. First, you can alias global variables. At the bottom, we reference the jQuery global variable, which is even better than referencing $ because sometimes people setup jQuery in no conflict mode, where it does not create a $ variable. But then above, we alias this to $, meaning it's safe inside for us to use that shortcut. You probably don't have this problem, but you'll see stuff like this in third-party libraries.

Second, when you pass in a global variable as an argument, it protects you from making a really silly mistake in your code, like accidentally setting $ = null. If you do that now, it'll set $ to null only inside this function. But before, you would have overwritten that variable globally. It's yet another way that self-executing blocks help to sandbox us.

Fun with window

Ok, back to this mysterious window variable. Inside index.html.twig, console.log() window:

... lines 1 - 64
{% block javascripts %}
... lines 66 - 69
<script>
console.log(window);
... lines 72 - 75
</script>
{% endblock %}

This is pretty cool, because it will show us all global variables that are available.

And Boom! This is a huge object, and includes the $ variable, jQuery, and eventually, RepLogApp.

But notice what's not here. As expected, there is no Helper.

Forget var? It goes Global!

Now, go back into RepLogApp, find Helper, and remove the var:

(function(window, $) {
... lines 2 - 55
Helper = {
... lines 57 - 68
};
})(window, jQuery);

You've probably been taught to never do this. And that's right! But you may not realize exactly what happens if you do.

Refresh again and open the window variable. Check this out! It's a little hard to find, but all of a sudden, there is a global Helper variable! So if you forget to say var - which you shouldn't - it makes that variable a global object, which means it's set on window.

There's one other curious thing about window: if you're in a global context where there is no this variable... then this is actually equal to window:

... lines 1 - 64
{% block javascripts %}
... lines 66 - 69
<script>
console.log(window === this);
... lines 72 - 75
</script>
{% endblock %}

If you refresh, this expression returns true. Oh JavaScript!

Be Better: use strict

Back in RepLogApp, forgetting var is actually a mistake, but JavaScript is friendly, and it allows us to make mistakes. In real life, friendly and forgiving people are great friends! In programming, friendly and forgiving languages mean more bugs!

To tell JavaScript to stop being such a pushover, at the top of the RepLogApp.js file, inside quotes, say 'use strict':

'use strict';
(function(window, $) {
... lines 4 - 57
Helper = {
... lines 59 - 70
};
})(window, jQuery);

Tip

Even better! Put 'use strict' inside the self-executing function. Adding 'use strict' applies to the function its inside of and any functions inside of that (just like creating a variable with var). If you add it outside of a function (like we did), it affects the entire file. In this case, both locations are effectively identical. But, if you use a tool that concatenates your JS files into a single file, it's safer to place 'use strict' inside the self-executing function, to ensure it doesn't affect those other concatenated files!

I know, weird. This is a special JavaScript directive that tells your browser to activate a more strict parsing mode. Now, certain things that were allowed before, will cause legit errors. And sure enough, when we refresh, we get:

Uncaught reference: Helper is not defined

Sweeeet! Even PhpStorm isn't fooled anymore, it's reporting an:

Unresolved variable or type Helper

Re-add var, and life is good!

'use strict';
(function(window, $) {
... lines 4 - 57
var Helper = {
... lines 59 - 70
};
})(window, jQuery);

Leave a comment!

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
    }
}