Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

var Versus let: Hoisting!

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

There's one other reason to use let instead of var. To understand it, we need to get really nerdy and talk about something with a cool name: variable ahoy-sting. I mean, variable hoisting.

At the top of the play file, do something terrible: console.log(bar):

13 lines play.js
console.log(bar);
let aGreatNumber = 10;
... lines 3 - 13

I know! This doesn't even make sense - there is no variable called bar! When we try it, we get:

ReferenceError: bar is not defined

No surprise! If we try to log aGreatNumber, the same thing happens!

13 lines play.js
console.log(aGreatNumber);
let aGreatNumber = 10;
... lines 3 - 13

The variable has not been initialized yet.

Ready for things to get weird? Change the let to var:

13 lines play.js
console.log(aGreatNumber);
var aGreatNumber = 10;
... lines 3 - 13

And all of a sudden, it does not break. It simply says that that value is undefined.

Hello Mr Variable Hoisting

The reason for this is something called variable hoisting, a term you'll see a lot around JavaScript... I think mostly because it has a cool name. It's actually not that important, but I want to tell you a little bit about it so you don't have to worry about it ever again.

In PHP, we never need to initialize a variable with a special keyword. We don't say var $aGreatNumber = 10, we just say $aGreatNumber = 10 and we're good to go. But in many other languages, including JavaScript, you must initialize a variable first with a keyword.

When you use var to initialize a variable, when JavaScript executes, it basically finds all of your var variables, goes to the top of that variable's scope - usually the top of whatever function it's inside of, but in this case, it's the top of the file - and effectively does this: var aGreatNumber. That initializes the variable, but doesn't set it to any value. This is called variable hoisting: and it's the reason that we get undefined instead of an error when we try to use a variable that's declared with var... before it's declared.

But when we change this to let, we already saw that this does throw a ReferenceError. And that's kinda great! I mean, isn't that what we would expect to happen when we reference a variable that hasn't been created yet!

So with var, variables are hoisted to the top. But with let, that doesn't happen, and that's kinda cool. Well, technically, let also does variable hoisting, but thanks to something called the "temporal dead zone" - also an awesome name - let acts normal: as if its variables were not hoisted.

Since let seems to behave more predictably, let's go into RepLogApp and change all of these "vars" to let. Find all "var space" and replace with "let space":

'use strict';
(function(window, $, Routing, swal) {
... lines 4 - 26
$.extend(window.RepLogApp.prototype, {
... lines 28 - 47
handleRepLogDelete: function (e) {
... lines 49 - 50
let $link = $(e.currentTarget);
... lines 52 - 61
},
_deleteRepLog: function($link) {
... lines 65 - 70
let deleteUrl = $link.data('url');
let $row = $link.closest('tr');
... lines 73 - 82
},
... lines 84 - 88
handleNewFormSubmit: function(e) {
... lines 90 - 91
let $form = $(e.currentTarget);
let formData = {};
... lines 94 - 104
},
_saveRepLog: function(data) {
return new Promise((resolve, reject) => {
$.ajax({
... lines 110 - 112
}).then((data, textStatus, jqXHR) => {
... lines 114 - 119
}).catch((jqXHR) => {
let errorData = JSON.parse(jqXHR.responseText);
... lines 122 - 123
});
});
},
_mapErrorsToForm: function(errorData) {
... line 129
let $form = this.$wrapper.find(this._selectors.newRepForm);
$form.find(':input').each((index, element) => {
let fieldName = $(element).attr('name');
let $wrapper = $(element).closest('.form-group');
... lines 135 - 139
let $error = $('<span class="js-field-error help-block"></span>');
... lines 141 - 143
});
},
_removeFormErrors: function() {
let $form = this.$wrapper.find(this._selectors.newRepForm);
... lines 149 - 150
},
_clearForm: function() {
... lines 154 - 155
let $form = this.$wrapper.find(this._selectors.newRepForm);
... line 157
},
_addRow: function(repLog) {
let tplText = $('#js-rep-log-row-template').html();
let tpl = _.template(tplText);
let html = tpl(repLog);
... lines 165 - 167
}
});
/**
* A "private" object
*/
let Helper = function ($wrapper) {
this.$wrapper = $wrapper;
};
$.extend(Helper.prototype, {
calculateTotalWeight: function() {
let totalWeight = 0;
... lines 180 - 184
}
});
})(window, jQuery, Routing, swal);

And just to make sure that our code doesn't have any edge cases where var and let behave differently, try out the page! Yay! Everything looks like it's still working great.

Now, what about the new const keyword?

Leave a comment!

0
Login or Register to join the conversation
Cat in space

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

This tutorial uses Symfony 3. But, since this is a JavaScript tutorial, all the concepts work fine in newer versions of Symfony.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.0",
        "symfony/symfony": "3.2.*", // v3.2.14
        "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.2
        "symfony/monolog-bundle": "^2.8", // v2.12.1
        "symfony/polyfill-apcu": "^1.0", // v1.3.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.19
        "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.2
        "symfony/phpunit-bridge": "^3.0" // v3.2.2
    }
}
userVoice