Buy

One of the crazy things about JavaScript... is that there's not one good way to loop over a collection! In PHP, we have foreach, and it works perfectly. But in JavaScript, you need to create an ugly custom for loop. Well actually, there is a .forEach() function, but it only works on arrays, not other loopable things, like the Set object we'll talk about later. And, with .forEach(), there is no break if you want to exit the loop early.

That's why we've been using jQuery's $.each(). But guess what? ES2015 fixes this, finally. Introducing, the for of loop!

It looks like this: for (let repLog of data.items):

233 lines web/assets/js/RepLogApp.js
... lines 1 - 2
(function(window, $, Routing, swal) {
... lines 4 - 37
loadRepLogs() {
$.ajax({
... line 40
}).then(data => {
for (let repLog of data.items) {
this._addRow(repLog);
}
})
}
... lines 47 - 230
window.RepLogApp = RepLogApp;
... lines 232 - 233

And it's pretty easy to follow: repLog is the new variable inside the loop, and data.items is the thing we want to loop over. We're no longer passing this an anonymous function, so we can get rid of everything else. That's it. Say hello to your new best friend: the for of loop.

Let's look for the other $.each() spots and update those too! Instead, say for let fieldData of $form.serializeArray():

233 lines web/assets/js/RepLogApp.js
... lines 1 - 2
(function(window, $, Routing, swal) {
class RepLogApp {
... lines 6 - 94
handleNewFormSubmit(e) {
... lines 96 - 100
for (let fieldData of $form.serializeArray()) {
formData[fieldData.name] = fieldData.value
}
... lines 104 - 111
}
... lines 113 - 212
}
... lines 214 - 231
})(window, jQuery, Routing, swal);

Before, the anonymous function received a key and then the fieldData. But, we didn't actually need the key: the $.each() function just forced us to add it. Now, things are cleaner!

Make this same change in two more places: for $element of $form.find(':input'). Ah, don't forget your let or var:

233 lines web/assets/js/RepLogApp.js
... lines 1 - 2
(function(window, $, Routing, swal) {
class RepLogApp {
... lines 6 - 136
_mapErrorsToForm(errorData) {
... lines 138 - 140
for (let element of $form.find(':input')) {
... lines 142 - 152
}
}
... lines 155 - 212
}
... lines 214 - 231
})(window, jQuery, Routing, swal);

Then, one more below: for let $element of $elements:

233 lines web/assets/js/RepLogApp.js
... lines 1 - 2
(function(window, $, Routing, swal) {
... lines 4 - 183
class Helper {
... lines 185 - 204
static _calculateWeights($elements) {
... line 206
for (let element of $elements) {
... line 208
}
... lines 210 - 211
}
}
... lines 214 - 231
})(window, jQuery, Routing, swal);

Oh, and PhpStorm is warning me because I forgot to remove one of my closing parentheses! And, we don't need that semicolon! Yay!

So, use the for of loop for everything! Well actually, that's not 100% true. for of is perfect when you want to loop over a collection of items. But, if you want to loop over an associative array... or object, and you need to know the key for each item, then you'll use for in.

Tip

Actually, you can use for of with an object, with a clever combination of Object.entries() and array destructuring!

let pets = {
  beagle: 'Bark Twain',
  poodle: 'Snuffles'
};

for (let [petKey, petName] of Object.entries(pets)) {
  console.log(petKey, petName);
}

BUT, the Object.entries() method is still experimental, and may be included in ES2017.

This is the one limitation of for of: it gives you the value of the item you're looping over, but not its key, or index. In fact, if try to use for of with an object, you'll get an error.

Leave a comment!