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.
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)
:
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!
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
:
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":
; | |
(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?