What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
Using ES6 in Your Node.js Web Application
by Jonathan Mills
ES6 brings many cool new features to JavaScript. In this course, you'll get to see the features you should be using in your Node.js development.
Start CourseBookmarkAdd to Channel
Table of contents
Description
Transcript
Exercise files
Discussion
Recommended
Course Overview
Course Overview
Hi everyone. My name is Jon Mills, and welcome to my course ES6 with Node 6. I'm a consultant focusing mostly on JavaScript technologies, and it's been fun to watch JavaScript over the last 12 months or so evolve and add all these cool new features that are now baked into Node 6. And so in this course, we're going to take a look at some of the cool new JavaScript syntax and JavaScript features that are included in Node. Things like syntactic sugar and some cool new shortcut notations, some built-ins like promises and generators and things like that, and we'll take a look at some function changes that include class and what that means and how that's going to work in ES6. Now, we're going to focus mostly on the Node side. Some of our examples are going to be very Node-specific, and I'll show you some examples even from my Web Apps course and how using ES6 is going to change some things that we are doing there. But you don't have to be a Node developer to get something out of this course. If you just want to watch through the course and see some of the ES6 stuff, that's cool too, just know that having some JavaScript experience and having some Node experience will help you follow along maybe a little bit better.
ECMAScript and Node.js
Introduction
In this course we're going to look into what's available in the latest versions of Node from an ES6 perspective, so what are the features and functions that we can use as we upgrade to the latest versions of Node. Now before we get started though, we're going to take a quick look at some basics in order to get everybody on the same page. So we'll start by talking about Ecma and what Ecma is and where they came from and how they're involved in the JavaScript area. And we'll talk about ECMAScript, because it's not JavaScript 6 or JavaScript 2015, it's ECMAScript. So we'll talk about what ECMAScript is and how that works with JavaScript. We'll talk about ES6 features. ES6 is just a bundle of new features and functionality that's been bundled into ECMAScript, and so we'll talk about what those features are, that's going to be the majority of this course, and what support they have across the multiple engines and across the multiple versions of Node. And specifically we'll focus on Node's ES6 adoption. And I'll walk you through for each feature which version of Node you need to be running in order to support the feature that we're talking about.
What is EcmaScript?
So what is Ecma? We talk about ECMAScript, but Ecma is something specific. Ecma is an industry association dedicated to standardizing information, it's actually the European computer manufacturer's association, and they work on building standards, and you can thank them for most things that are standardized across the computer industry. And we all like standards, USB devices, Bluetooth devices, things like that, Ecma is key for building standards associated with the computer industry. More than anything, they aim to facilitate creation of standards, and actually a lot of times when companies build a standard, they'll go to Ecma and say, hey Ecma, can you put a standard into this? And they also, once the standards are created, encourage the correct use of standards, and so they work with companies to say, hey, here's the standard you should be following, and let's make sure everybody's doing it the same way. And they are also the clearing house of publication. They publish and host the standards that they can give out to everybody else. So, with that being said, we lead to what's ECMAScript? So how does Ecma get involved in this whole big JavaScript thing and why is the newest versions of JavaScript called ECMAScript 2015 or ECMAScript 2016? So, ECMA-262 is the standard, so Ecma creates standards, ECMA-262 are the standards the JavaScript builds on top of. So, Ecma has published a set of standards, and so anybody who wants to implement those standards follows those, and JavaScript or the various JavaScript engines, and we'll talk about the engines here in just a little bit, are what follow these Ecma standards. Now, they're not the only one, Action Script, Microsoft's Jscript, there's other things that follow these ECMAScript standards for implementation. Now just to give you a little taste of what this means, this is an example of what the ECMAScript standards look like, and they're kind of painful to be honest. It's very legal, it's very, here's the way this works, and so this is just a little snippet of the basic rules of semicolon insertion. So if you watched my best practices talk, we kind of talked about this. And the JavaScript engines read these standards, and this is how they build the language of JavaScript on top of it.
What is ES6?
All right, so what is ECMAScript 2015 or ES6 as you will find a lot of people call it? ES6 has kind of gone in and out of favor. ECMAScript 2015 is the official title, but that's too long honestly, and so a lot of people just refer to it as ES6. Both are equally fine, it doesn't matter. But ultimately what is ECMAScript 2015? What is this phrase and what does that mean? Well, the first thing is that it adds a new set of features to the Ecma 262 standards, and this is very important, because ECMAScript is not a new language, you're not changing really much of anything, you're just adding a new set of features to the existing language. Now the way some people implement these changes, like ES6 modules, which we're not really going to even talk about in this course, but I'll talk about why that is here in a little bit, change some things fairly fundamentally, but for the most part it's just a new set of features to the 262 standards. You can adopt all or only some of these features. You could choose to only pull in let, or you could only do promises, you don't have to do everything, this isn't an all or nothing upgrade to ECMAScript or JavaScript, you just kind of do the pieces that you want. But here's the thing. It's up to each JavaScript engine to support each feature, which can be extremely painful on the browser side, because the Firefox engine may not support something that the IE engine does, or the Chrome engine, and so we're a little bit easier on the Node side, because own that environment, and we can decide what version of Node we're using, therefore, what ES6 features we can use. But with all that being said, this can be kind of painful, and so kangax is our friend in all things, because this is a website that's going to lay out for us all the ES6 features and how everything works, and what supports what. And so let's take just a few minutes to give a run-around of what kangax is and how that works.
Getting to Know KangaX
All right, so let's talk kangax. This is probably the best website you'll ever go to, and also probably one of the worst websites you'll ever go to, and this is why. It's just a big Christmas tree grid of greens, reds, yellows, oranges, that help us understand what features of ES6 are available where. And so, a couple things I want to point out to you really quick as we look at this page. First and foremast, I'm going to uncheck this Show unstable platforms, let's get rid of that, and that kind of it neatens it up a little bit, and we'll kind of go from here. There's three things I want to point out. First of all, across the top, we have all of the different JavaScript engines, V8, Spider Monkey, Core, Chakra, all of those, and the one that's going to matter for us on the Node side is V8. So, that's the green one, and here you can see Node over here for its support. Now there's Firefox, Spider Monkey, Edges, Chakra, and so on, and then we also have our polyfills or Babel our compilers that will help convert stuff from ES6 back to ES5. Okay, so that's that. Across the top here, we have ES6 compatibility percentages, just how much of the ES6 features we get access to on different things. So if you look at Chrome 51, 98%. If you look at Node, Node 4 was only about 50/50, Node 6 is about 93%, and notice almost nothing is 100%, because not very many people do the proper tail calls. That's one thing that just kind of kills everything. All right, two other things to look at. Down this side, so it's really one other thing I'm going to show you in two different ways, this is going to be the meat of this course, we're going to talk about most of this stuff. So if you look right here, default function parameters, and this is why this website is so incredibly awesome. I can click this dropdown, and it will show me all of the things around default function parameters. It will also, in this block right here, whether or not my current browser, note current browser, supports this functionality, and it does that by running a test. It also will tell you what other browsers, or Node in our case, support this feature, and so notice Node 6 supports default functions, whereas Node 4 and 5 do not. So if you're using Node 4, you can't do default functions. But what are default functions? And this is the cool part. If you hover over this, this is the test that is run to turn this green ______green or red. So the fact that this passes returns a function, a = 1, b = 2, that tells you whether or not your browser supports this, but this also just gives you an insight into what this functionality means. So if you scroll down, you can get into arrow functions, and all of the different things that arrow functions do, but ultimately you can just hover over it and say hey, here's what an arrow function is, here's what it looks like, those types of things. If you click on it, it's going to take you out to, this should look familiar to, ECMA 262, and this is actually the specification around arrow functions. 14.2, and it actually goes on for quite a bit, and this is the legal terminology that defines how arrow functions operate. So if you're ever bored, or you really want to go to sleep, start reading that stuff, it'll put you right out. All right, so this is kangax. And what we're going to do in this course is we're going to walk through each block, so we'll start with syntax, that's going to be a module, we'll do functions, we'll do built-ins, we'll do all of these pieces as we go through, and I'll just show you implementations so that you know how to work with these and the right implementations they are, especially in the Node environment. Although really, if you're not working in the Node environment and you're just over on the browser side, that's totally cool, all of this stuff works in both places. The other thing on this page is notice we're on 6, if we go to Next, this starts to show us the ES Next stuff, so some people call it ES7, some people call it ES 2016, whatever you want to call it, notice Array.prototype is actually supported in Node 6. None of the exponentiation operators are supported yet in either of them, although Node 6 does pick up some other things, like this one down here, the object.prototype getters and setters. But for the most part, we're going to focus right here on all this ES6 stuff.
ES6 Adoption in Node.js
So as we work through this course, it's important to remember and understand the ES6 adoption in NodeJS, and it started with I/O JS, which is now merged back into Node Core, and they broke off from Node for a lot of reasons, but part of it is because they wanted faster adoption of some of these cool new things. And so I/O had some adoption for ES6. When they merged back together, it became Node 4, which confused some people, because it was Node 0 and now it's Node 4, but Node 4 had about 50% adoption, and when we looked at kangax we looked at this a little bit. Node 5 had just a little bit more at 60%, and Node 6 has almost total adoption, almost everything's there in Node. And so what I want you to look for as we go through this course is for each thing that we talk about, I'll put an icon down on the bottom, one of these symbols, Node 4, Node 5, Node 6, or all of those symbols, so you know which versions of Node you need to be running in order to support the feature that we're going to talk about.
Summary
All right, so we made it through all of that fairly quickly. Just to basically get down to this point. We're going to work through the kangax feature list. We're going to go through each item in that list and talk about what they do, how they work, how to implement in your Node application, and again, if you're not doing Node, but you're watching the course anyway, that's totally cool, we'll show you how you can use that in whatever JavaScript implementation it is you're using. And each section is a module, so as you're working through that kangax list, each section in that list is going to be a module in this course. And I'll let you know as we begin each feature what's allowed and when just by throwing up a little icon at the bottom of the slide 4, 5, or 6, so that you know what version of Node you need to be running in order to use this feature.
Some Syntactic Sugar
Introduction
This first section of ES6 features that we're going to be talking about are basically just some syntactic sugar. Just some nice little syntax changes that we have that are going to make our lives a little bit easier when it comes to writing code. Nothing earth-shattering, nothing really feature-changing, just some cleaner implementation of some things that we do every day, and a lot of it's just shortcut notation, but it's cool when you get right down to it. And some of the things you can do that used to be even a few lines of code, now you can do just with a couple of key strokes, and it's really, really cool. So let's take just a little bit and look through some of these cool syntax changes that we have in ES6.
Function Defaults
Now, the first syntax change we're going to talk about is the idea of function defaults. And so let's talk through just a little bit and see what we're talking about here. So, here I've got a function called sum that takes two parameters and returns the sum of the two parameters, so, a really, really straightforward function. But here's the deal. Function parameters are a little weird in JavaScript, because there's no validation. In C# or Java or something, if I call a function that requires two parameters and I only pass in one, I get a syntactic error, but here I don't. It just runs, and in this case it returns not a number, and because Y is undefined and so 0 plus undefined is not a number. So what we end up having to do is do some validation code, and it can be ugly validation code. So in this case, if Y is falsey, then I'm going to set it to 0, and if X is falsey, I'm going to set it to 0, and in this case it works right up until I have some potential issues around whether or not Y is supposed to be falsey, right? So, if I'm passing in something that is false and false is a valid value, now I've got a problem. And so really what you want is whether or not it's undefined. And that makes your validation code even worse, and that's even grosser. So, what they've done in ES6 is they've added this idea of function defaults to simplify this validation process. What that means is that now I can just say right in the function, hey, if I don't have a Y passed in, then I'm going to set that to 0, and if I don't have an X passed in, I'm going to set that to 0, and that's just going to be what it is. And so this way now instead of having X + undefined or 0 + undefined, I have 0 + 0, and that just greatly simplifies our code validation and pulls that if undefined stuff out and puts it up inside of our parameters in a much simpler and easier way to read. Now you can take this even one step closer. So now I've got a function of product, and product takes two parameters, X and Y, but in this case, I can actually say, hey, if you don't pass in a Y, then we're just going to square it, we're going to multiply it by itself, and so I can right here say Y = X and it will take whatever was passed in as X and add it to Y. So you can become a little bit fancy with your function parameters and make that work even better.
Rest Parameters
To keep going with this idea of parameters, let's talk for a minute about rest parameters. And basically what rest parameters deals with is this fact that the number of parameters passed into a function is not set. In JavaScript, I can pass in 1, I can pass in 100, it doesn't matter, and the function needs to be able to deal with whatever's passed into it. And so let's set this up by talking about this. So I've got a function called log that takes a level and a message, and in this case if levels debug, we're just going to spit out a message. And this works great right up until I want to do this. I've got an object, which is equal to, we'll just do this. Now if I want to log out this object, a lot of times we'll just do something like object + obj. And now what's going to happen when we run this is it concatenates the object as a string, and so I get this weird object Object thing. And that's not really what we want. And so what console.log does, you can actually do this in console.log, is takes them as separate parameters. And that's super cool, right up until now I've got to deal with the fact that I could have multiple parameters in this one method, and so now I've got to deal with this. And the way up until now that you've had to deal with this is by using this thing called arguments. So what you do is you delete this, and then you'd use arguments, I'll just print this out, let's look at what arguments means, look at what I've got. So, arguments now looks like this. It's not an array of the arguments, it's an object that contains all of the arguments, which is 0, 1, 2, 3, and that's not necessarily awesome for a couple of reasons. One, it's not a true array. So things like slice don't work on it, and I don't want to spend a whole lot of time digging through all of the issues with this more, I just want to show you a different way. And remember, I'm not saying you can't do it in ES5 using arguments, you totally can, but in ES6 we have a little bit better thing, because notice too arguments includes are 0, as well as 1, 2, 3, and I don't necessarily want or care about the 0, because I'm taking in level here. I just want the rest of the arguments. I have level, I want the rest. So that's what ES6 gives us with this concept of rest parameters. So check this out. Rest parameters introduces this … syntax. And basically it does exactly what the name implies, it gives you the rest of the parameters. And it takes first, second, and third in this case and creates an array out of those objects. So basically if you pass it in 1,2,3, you get an array of 1, 2, 3, and that's going to make our lives a lot easier when we start looking at this, so check this out. So in this case, I can just do …args, and now if I log this, and we'll just print out the first one here, when I run this, look at this, a nice simple array, that was my object. Done. And so that way it's just a little bit easier to deal with args, and being able to pull in multiple args, because now what we do is we loop over args and print them out. Alright, so that's rest parameters. Now let's look at the opposite idea in the spread operator.
Spread Operator
So ES6 also provides us with the opposite idea of the rest parameters with the spread operator. And so basically all the spread operator does is it changes an array to a list of CSV. So, the rest parameters took the comma separated values of the parameters and turned it into an array, the spread operator does the opposite thing. So let's take a look at how that works. So basically we have the same thing we were working on in the last clip, we've got a log function that takes the level and an args and just dumps the args out to the console. Well, that's great, right up until I have an array of objects. So let's just do this. Okay, now I've got an array of objects. Well, my function doesn't take an array, it takes a CSV, just comma separated values for the parameters, and so now in order to print that out, I would have to kind of loop over and end up doing something like this and spread it all out so that my logs up top would deal with it appropriately. Well, we have the … syntax to pull it apart, we also have the … syntax to spread everything out. So, if I do this, now I get a CSV or a comma separated values of this list. And so actually if we go and run this real quick, now look, I have an array with a and b. So my args actually spread everything out and do it appropriately. Now there's also another interesting thing we can do with this idea. Let's just create another object called foo. And I want to add this to the front of my object. So, I want to add foo to the front of object, well, there isn't a really super great way to do that in ES5, there's a great way to push it onto the end, but not necessarily to add it to the front. But now I can actually do this, I can say obj = foo, and then obj, because all this is going to do is print out and spread this out into extra commas. So what you end up with essentially is this … object just becomes that. It just kind of blows it out there. And so now when I print this and I run this again, you'll see hey, I've got my object sitting in the front. So that's spread. Spread and rest kind of do opposite things, but in the same way. So up in the parameters it brings commas back together, and down here where I'm passing it in or I'm doing it inside an array, it spreads it back out again.
Object Literal Notation
Probably my favorite shortcut piece of syntax in ES6 is the way object literals are handled now. And it's really simple, just shortcut syntax is really all it is, but it's super cool. So check this out. If you're anything like me, all through your code you have these revealing module pattern things. So, here I've got a greeting service with two methods on it, sayHi and sayBye, and I'll just fold those up. And then you have this return statement, you're returning the object, and so if I just do this, just log the return of the greetingService, you see I have sayHi with a function and sayBye with a function. And this is just normal. We've got it everywhere. Now this repetition sayHi: sayHi, sayBye: sayBye is just kind of dumb, and we're just kind of used to doing it, but now in ES6 we no longer have to. I can just say, hey, I'm going to return sayHi and sayBye and be done, and just have that. And if I run this again, look, see, it still works. So it just knows, hey, if I have a variable named sayHi, I'm going to pull in whatever is in that variable and just name it that. Now I can even take this one step further and just take this entire function and just do that, sayHi console.log, there it is, just like that, and I can take this function, and this is more the way some people do their revealing module pattern, they just kind of lay it out this way, but still it just works. You lay it out, no more colons and explicitly stating what your object literal names are, it just kind of works. So that's the new object literal syntax, it's just simple, straightforward, shortcut syntax, no functionality changes here, it just shortcuts the way that this works and makes it a little bit cleaner and simpler to write things out.
For of Loop
Another cool little shortcut syntax that we have available to us in ES6 is the For Of loop, and it's just a simple way to iterate over an array or string instead of doing the more longhand for loop. Alright, so let's look back at our log function that we've been using kind of throughout this course, and here we've got function login. Now I've actually implemented the for loop, and so this is the standard way to iterate over an array, var i = 0, and I know that I'm not using let, I haven't talked about that yet, that comes later, i is less than args.length, all of those things. Now this is kind of painful, not hugely painful, but just, it's ugly, and I'm introducing a new variable and it's just kind of gross. Well, what we have now is a much simpler way to do this. Now I can just say for item of args. And this is just a variable, it could be anything, but for, we'll just call it, we can even do for i of args, and instead of args i, and I just print i. That is a lot simpler than the other way. And look, it still works. So, for of loops are just a simple little way to print out an array. And like I said, you can even use strings, if I just say of level, so level's just the string debug, I'm going to get each character printed out, right? So, it works in a couple of different ways, and we'll put that back to the right way, but iterating over things is what we do all the time, and just the nice, clean, simple little way to make that happen.
Template Literals
In my mind, the most slam-dunk, easiest to use ES6 feature are template literals. This is something that has been sorely lacking in JavaScript up till now and is just a huge readability and simplicity thing that we can add. So here, check this out. Let's go back to our greetingService for just a second. So I'm doing right here something that everybody does everywhere. I'm concatenating a string, and here I've got Hello + name + exclamation point. And this is, in this case not so bad, but in some cases, especially when you're concatenating API end points and things like that, this becomes horribly, horribly unreadable, and it doesn't look very clean. What ES6 introduces is the idea of the backtick. Now, the backtick is above the Tab key, and you can basically just get rid of all these plusses and close your backtick, and now here where I have a variable, I use dollar sign, open close parentheses, and then I just pass the variable in and that works. So if you come back down here and just kind of finish this out and actually create an instance of our greetingService and then do gs.sayHi, when I run this now, I get my Hello jon. Oops, I have a space here, and see, you can see that. It's a little bit easier to see that now. So let's take that space out, there we go, now it's working. Alright, so backticks are what we use to make this whole thing work. So we get rid of all the tick marks and all that stuff, we'll just do the same one down here, backtick, dollar, curly, curly, and then put your variable name in there. Now, one thing to keep in mind, and this is very important, the backticks will treat everything that occurs until the next backtick as part of the string. And that's a fundamental distinction from how things have happened in the past. Because if I do this, now when I run this, you'll notice that now it's on a new line. And this is weird, and it's going to look weird, and it's not cool, but that's the way this works. And if I tab this over to kind of line things up, all these tabs right here, there you go, they stuck in there. So, that might be useful to line some things up and kind of outline how a string looks in some cases, but that's a little less usable than just being able to stick a variable name in there. So just be aware, that's how that works. All of the space, period, or all of everything between one tick mark and the next tick mark is what shows up on the screen, unless you use the dollar curlies, and then it'll change the variable name. This is probably, in my opinion, one of the best editions to ES6 is to get rid of those plusses, plus tick plus, so that we can now just write our strings as normal and drop variables in.
Destructuring
Now the last one we're going to talk about is destructuring, and this one takes a little bit more getting used to, because it's very much a shortcut syntax that doesn't necessarily provide functionality, it just is a way to shortcut pulling out pieces of objects or arrays or things like that. So let's look at this real quick and then I'll show you a practical application that you can look at to see where this might fit. Alright, let's come back to our greetingService for a second. And if we look at our code, so greetingService is returned sayHi or sayBye, if I follow that all back up, notice how we only use the sayHi of our greetingService, and so if greetingService was bigger, I don't necessarily want to carry that around everywhere, I just want the piece that I'm going to use. And so what I actually can do, what I could do is I could just do sayHi = greetingService.sayHi, and only pull in that one thing. And so when I run this, there we go. So everything still works, and I no longer have the entire greetingService being used in my application, it's just this one little thing. Well, this is a little bit convoluted and it's not clean, and so what they've given us is this shortcut method to do exactly this same thing. Instead of putting .sayHi on the end, I can just come over here, add a curly brace, add a curly brace, and now that's going to pull apart the greetingService and add sayHi as a variable with the contents of greetingService.sayHi, which is a longwinded way of saying right here I now have just the sayHi piece of this greetingService, and I can just execute it like that. There we go. Now one of the reasons why this is also a little bit cleaner is because right here inside these curly braces I can also do sayBye. So let's say if greetingService had a whole bunch more stuff in it and I just needed two pieces, well now instead of executing greetingService twice, so if I was going to get .sayBye and then another one that says .sayHi, I can execute it once and pull back both of these variables, and now both of these things work. So another feature of this destructuring is I can give them variable names, I don't have to name them exactly the same thing that's pulling out of our greetingService. So I can just do hi and bye, just like that. There you go. And now that works. So destructuring, all destructuring means is that I can pull pieces out of an object by using this curly brace syntax, and I'll get rid of the sayBye just to make it look a little bit better, we'll pull that over so you see the whole thing, I can just use the curly braces to pull out a piece of an object instead of having to pull back the whole thing. Now, if we play around for just a little bit, this same thing works if I do an array. So if I just want the first piece of an array, now I have pulled this 1 out, and so now you see that's a 1. Or if I want the second piece of an array, I can actually kind of just do that, and now I get the second piece of the array, right? So, destructuring is the same idea as what we were just talking about, but now I'm doing it with an array. So if I have an array of configuration items, or I have an array of whatever it is we want, I could actually just pull them out, I pull out the first three pieces of the array to go do something else with it. So, it works the same way as the object, but now I'm using it in an array format. Alright, so let's look at something real that you might use this in so that it adds just a little bit of context to what we're talking about. So, in this example, I'm going to use my book controller from another course I did, Web Services with Node.js and Express. And in this, I have a goodreadsService, and this service is what's going to go out to an API and pull it back. Well, in this API, or in this service, I have a method called showBook, and I do a module.exports, and I run a function, I execute it, and I pull back this one thing. Well, in my book controller, notice I'm requiring my goodreadsService open and closed parentheses, and I've got my book service here. Now the only thing I use is bookService.showBook, down on line 38, this is it. That's the only thing I use. And so I'm carrying around this entire bookService object, when really I just need the one function. And so what I can do is do this destructuring thing on my require. That basically is going to take what the require that comes back drop just in the showBook object, or variable, and now I can get rid of this, and everything just works. So that's a great example of how you can use destructuring to kind of clean up these big services or these larger things that you have by just pulling out just the piece you need in a cleaner way to drop it in here. Now if I'm only doing one thing, you might say, yeah, just stick it on the end, I could easily just say .showBook here, where this becomes a little bit more convenient is when you have maybe two items. And so I can do showBook and addBook, and then I don't have to worry about all the rest of it. Alright, so that's destructuring in a nutshell. You can make this more complicated if you want to, and you can look at kangax to see all the extra examples of it, but really at its core, this is what destructuring is all about it. It's basically just a way to pull something out of an object or an array into a variable directly. So that's ultimately it.
Summary
So that's it for this first piece on just some syntax changes. Now there isn't too much that's earth-shattering here, and no real functionality changes, it's just a cleaner implementation of some things you do already. And with the exception of the multi-variable destructuring that we just talked about in the last clip, you can do all of this stuff in ES5. What ES6 is providing us with is some cleaner implementation or some shortcut notation. So, you can use a for loop and that works just fine, but for of is just a little bit of a cleaner implementation or just kind of a shortcut way to do things. So, hopefully just with this one module, you've seen a whole bunch of stuff that makes sense just to start using ES6, just some quick ES6 shortcuts to make your life a little bit easier. Let's move on now and talk about changes in ES6 that are some functionality changes that will solidify your code a little bit more and make your code a little bit cleaner and more reliable.
New Bindings
Introduction
So now that we're done with the syntactic sugar type changes in ES6, let's talk about the new binding options we have in ES6, and this is actually going to be some functionality changes and some changes in behavior on the way variables work in ES6. So this is not just sugar anymore. The whole last module was stuff you could do anyway; this is different, and this is no longer just a different way to do the same thing. We're going to make some actual variable changes now. The way variables work, or the way some variables work in ES6 are going to be fundamentally different, and the reason for that is they're fixing some scoping issues. In ES5 we don't have block scope, and in a lot of other languages, you have block scope, and since JavaScript is probably not your primary language, we forget that we don't have block scope. And so we've got to find a way to fix that now in ES6.
Block Scope
All right, so the first thing we're going to look is this idea of block scoping, and now we actually have block scoped variables with the let keyword. So let's look at what this means a little bit. So, here I am declaring a variable called x, and I'm setting it equal to 1. Then, we'll put in a whole bunch more code, because usually that's what happens, our JavaScript files don't have nine lines of code, they've got hundreds of lines of code sometimes. And then I've got a for loop, and I do var x = 0, because in most other languages, that is protected. I can do var x = 0 and x only exists inside the scope of this for loop, and then I run through that, and then down at the bottom, I'm using the x. And I would hope that I'm using the x from way up above on line 1, but I'm not. Well, I mean, technically I am, because on line 5 I didn't create a new instance of x. Even though I've got the var keyword out in front of it, it doesn't matter. So when I run this, notice x = 10, and x = 10 is not what I would have thought, because I want x = 1. Okay, and so that's the issue with block scoping in JavaScript, because it doesn't exist. So it's not that I have an issue with block scoping in JavaScript, because there is no block scoping in JavaScript. And so what we have introduced now is this keyword let. And what let's going to do is it's actually going to declare that variable inside block scope. So it's not going to be hoisted, it's not going to pop up anywhere, it's just going to exist inside the confines of this for loop and then go away afterward. And so now if I run this again, notice now x = 1. So, that's let. It really is just that simple. The key is going to be what do you do with it? Because what some people jump to, and it's fine if you do this, is now everything becomes let. No more var, var goes away, everything's let, and that's cool, because that still works, and that's fine, you can totally do that. I don't do that. Not for a really, really good reason, just because I choose not to. I leave var for everything, and use let to denote, hey, this is a block scoped thing. And that, I don't know, may be a dumb reason, and I'm fine with it, you can call me out on it and we can talk about it, but for me, I'm going to use let to denote these things are blocked scoped. And I'm going to use var for everything else. And if you choose to say, hey, I'm just going to use let everywhere, that's cool. You can totally do that, and it'll be fine. So let's be clear, let is just going to sit here, and if you use let here, these are no longer hoisted. So let's look at that and let me show you what that means. If I do a console.log x right here with a var, x = 1, what's going to happen is when I print that out, I get an undefined. And I get an undefined because x exists, it just hasn't been assigned a value yet. What actually happens, or pretend this is actually what happens, it's not really, but that var x moves up here and this just becomes x = 1. Now if I use let, this is no longer hoisted. So what happens now is I get a big fat blowup, because x doesn't exist yet, so x blows up. So, that's the other distinction with let. Let is no longer hoisted like var was. And for some people that's just too much change, and that's fine, let's only use let here, but for some people you expect it to work that way, so let's just use let everywhere. And really that's going to be a personal choice, whatever makes sense for you. But that's let, and that's the fundamental changes around how let works with variable declarations and hoisting and block scoping.
Const
A slight variation of the let keyword is the const keyword. And basically what this lets us do is create constants using that same block scoping mentality. So if I create a new variable with const, and let's say I'm doing an env = 'debug.' So that's totally cool. Now if I set env = 'prod,' I'm going to get an error. I'll get a blowup that says, hey, assignment to a constant variable not allowed. So, const is basically just a way for us to create constants, just like most other languages that you've probably played with, you've got an option to create a constant, here you go. Now const is block scoped, so if I say if true, and now I can create a new const called env = change that to 'prod,' and now I've got two different env variables and then I'll print prod, then I'll print debug. There we go. So, const really is just that simple. Cost and variable, block scoped works just the same way let does, except you can't override it again.
Summary
So that's it for the variable changes. It's really just that simple. Just a couple of variable changes to help fix some scoping issues with ES6. And that's let being fixed the block scope thing, because most other languages or the languages you've probably been using, .NET, Java, have block scope, and so now ES6 presents us with that block scope option. They also threw in the constant just in that same vein of block scoping, but we now have a constant that we can use to help prevent things from being overwritten. So that's that. Let's pop ahead and look at some function changes that might provide some cool functionality for us as well.
Function Changes
Introduction
In this module, we're going to start talking about some function changes or some different function types that exist in ES6. We'll start by talking about arrow functions, and this is a cool new shortcut notation to do functions. Now the awesome part about arrow functions is it changes the this binding to more of a lexical binding, and we'll talk about what that means here in just a little bit. But arrow functions in callbacks especially start to behave so much better and it helps us out quite a bit. We'll also talk about classes, and we're talking about classes in a module about functions, because ultimately in JavaScript that what classes are. If you're coming from a C# or a Java background, classes are different in JavaScript than they are in these other languages, and we'll talk about what that means and how they're not exactly what you're used to, but they may be a useful way for you to create objects in JavaScript if they're going to fit the model that you need.
Arrow Functions
So we'll kick this module off with a conversation about arrow functions, and this new cool little shortcut syntax we have for creating functions. And I say it's cool only because later we're going to talk about some really, really nice implications for services and callbacks and things like that, but let's start out with just a basic conversation about arrow function. So here I've got a function called sum, and it takes two variables, and returns a + b. So, it's really just that simple. And if we run this, you see, we get a 5. That's cool. Now an arrow function basically is going to be this exact same thing, except we're going to do an expression, so var sum =, and then instead of putting function here, we're just going to do this. And now I have a, b, and actually we'll put this all on one line, just there you go, a, b arrow, return a + b, done. Now I've got a function called sum that does exactly that. Now if that was it, then that's not all that super cool, but check this out, if I only have one line, which I do in this case, I can actually get rid of everything around it. If I have more than one line, I need to keep my block, but if I only have one line, now I can shortcut notation that to just a + b and it still works. Same thing, a + b. Now the parentheses on the other side are also kind of optional if you have only one parameter. So let's actually make this square, and my arrow function is going to do that. Now when I run this, I'm going to get a 4. So, arrow functions essentially come down to just a quick little shortcut way of writing a function. And if it's one line and one variable, if you have no curly braces, no parentheses at all, if you have multiple lines or multiple things, it's just a way to eliminate the word function really. So, that's arrow functions. And at this point you might think that's not really all that cool, because honestly it's really not all the cool. Where the cool part of arrow functions comes in is when you're dealing with objects and callbacks and the this keyword. So let's look at a little bit more complicated example so that you can see how arrow functions really start to take off.
Lexical Bindings for this
Aright, now we're going to make things much more complicated with our arrow functions, and I've gone back over and we're back in our GreetingService. But I've changed things up just a little bit. Now I'm using a constructor function, which is why my G is capitalized here, and I've got a GreetingService that's going to be new dot. So back here on line 19, I have gs = new GreetingService jon. And what that does is it creates a new this scope, and it does all that for me, and so now my GreetingService has a this.name, and when I pass the name in, I assign name to this.name. So all that's great. When I do a gs.sayHi, I'll unfold this, I'll console.log my this, just so you can see what's going on, but then I'm also going to do my Hello this.name. And when I run this, you see my GreetingService has the name and my three functions, and then I write out my Hello jon! So that all works great. And as long as we don't go any further than this, we're fine. The problem comes in with a couple of different scenarios, the first one being do you remember when we talked about destructuring? I don't want all of the things around GreetingService, I just want sayHi. So I'm just going to pull sayHi out of my GreetingService. Now when I run it, I'm going to get undefined and look at this, my this is now this huge big thing, and it's the process, it's the Node process that's running. And I get Hello undefined. And the reason for that, if I back up for just a second, the way this is bound is not by closure or anything that happens up inside the GreetingService, this is bound to whatever's on the left side of the dot. So in this case, gs.sayHi, gs is what the this is bound to. When I destructured everything, there's no dot anymore, so this is no longer bound. And you might just say, well, that's a reason not to destructure, and that's cool, but if we set everything back to how it was, we also have an asynchronous version of sayHi, and what this is meant to mimic is when you're calling an API or you're making a database call, or you're doing something like that, well now you have a callback. So, sayHiAsync executes that timeout, that's just to mimic our database call or API or something, and then executes this callback. And this callback also references this.name. And if I do it just exactly the same way I did before, gs.sayHi, but now I do gs.sayHiAsync, now when I run this, same thing happens, Hello undefined, and I get my big process as my this. Now to make matters worse, if I'm doing use strict, now it's even weirder, because I get the timeout this, and it's still undefined. So, everything's kind of weird here, and we don't necessarily like this. So, this is where arrow functions come in very handy. And the reason for that is the way this is bound, because as I said just a second ago, this is bound to whatever's on the left side of the dot, so in this case gs.sayHiAsync, this is the GreetingSerivce. Well, this callback, when this function is executed, when this function right here is executed, there's nothing on the left side of the dot, it's just executed out in the open, so you get the global scope. Now here's what we can do. Arrow functions are bound lexically, so the this keyword in the arrow function binds back to the creating function scope. So I've got a function here, this function, when I use an arrow function, this function becomes the this binding. So if I change this, instead of function, we get rid of function remember, and I just do => and then keep my same function, everything else is exactly the same, I've done nothing different, now when I run this, it's going to work just fine. Hello jon! Boom. And I get my GreetingService. This is a massive game changer for callbacks and things like that, especially when we start talking about classes. And the other advantage too, so that's just with callbacks, now let's do the same thing with my regular old sayHi function, so we'll get rid of the word function, we'll add my arrow, nothing else is different. Now I'm going to destructure, and I'm just going to pull in sayHi. Now when I execute this, boom, same thing, still works. So this is where the arrow functions become hugely helpful. And they kind of open up a whole big series of opportunities when it comes back to using the this keyword, instead of doing a dot bind or some other things, we just use an arrow function in our callbacks to make things a lot simpler and a lot easier for us to do. So, for me personally, I don't use arrow functions everywhere, I just use arrow functions in my callbacks, because especially in my Angular controllers where everything's this bound and it's all bound back, instead of taking a copy of this, so the other way we would have done this in sayHiAsync if this was still a function, right, we totally could have handled this differently, you just take a copy of this. You say var ctrl = this, just like that. If you've written Angular code at all, you've done this, you know you have, right? And then down here you just do ctrl and ctrl and it all works. And that's cool, but it's kind of a hack. And the arrow functions help eliminate our need to do that. So that's arrow functions in a nutshell.
Prototypes
Before we start our conversation on classes, let's take a quick look at constructor functions and prototypes and the way those work, so that we understand what's going on when we're talking about classes. So here we have a constructor function called Task, and I denote it as being a constructor function by having a capital T on the Task, that's just one of the ways that we denote, hey, this is a constructor function. And what Task does, the whole purpose of being, is to take a name and then be able to tell whether or not it's complete. So if I call Task.toString, then I print out whether or not that task is complete. Pretty straightforward, simple task. Now, down here, I'm creating two instances of task, and I'm doing a console.log of the toString. Now here is an important thing. I said two instances of task, and that's not really how this works in JavaScript, that's more of a C# term, I'm creating two copies of task that have their own this scope, and that's pretty much the best way you can describe it, but task1 and task2 have their own scope. And so when I run this, what you're going to see is module 1 and module 2, neither one of them are complete. And then, task1.complete = true, now when I print it out, look, module 1 is complete, module 2 is not. Okay. Now this is the way a lot of times we build things, but if you're going to create many instances of a task, what we do in JavaScript is we drop things down to the task's prototype, and the reason why we do that is we don't want to have a whole bunch of copies of this function. Right now this function, the toString function, exists twice. I have two copies of this code, one for task1 and one for task2, and that's not really how we want it to be. So, what we do is we take this function, we cut it out, and we come down here outside of the function, and we create a prototype on task called toString, and then our function is going to be our old function. Now task1 and task2 both point back to the same version of toString, the same exact thing. If we run this, it still works, everything's still fine. Now here's the struggle with prototype, and I'm saying this out loud so that we talk about classes we're all on the same page. There's two issues. Right now the only way for the prototype to interact with the task is through the this keyword. So I've got this.name = name, and then down here I'm referring to this.name and this.complete. There is no option for me to have a private variable. If we come back to where we were before and take everything off, I can create a private variable, and inside my toString function, I could print out how often this toString function is called. And if we run this, you'll see I called it once for each, and that's actually a good way for you to see it's two separate functions, right? It's called two separate times. Now here's the struggle with doing it the prototype way. I can't reference this called variable from inside my prototype. The option of a private variable or something not on this scope doesn't exist. So, with that being said, let's talk about classes and how classes work, and so that you can see that kind of laid out and then we can talk about whether or not classes make sense for what you're trying to accomplish.
Classes
Now that we've briefly touched on objects and prototypes, we can now talk about classes. And if you want to dig farther into objects and prototypes, there's several other courses out there in Pluralsight, Kyle's Advanced JavaScript course is really, really awesome, or I've got a Design Patterns course that's out there, there's a couple of different options. If you want to dig more into that, I suggest you go try one of those as soon as you're done with this course. But we've touched enough to where we can start talking about classes. So here I've got my constructor function still with name and toString, nothing's changed since the last clip, but now instead of doing it this way, I have this new class structure. So instead of a constructor function, I actually can just say class, class Task, and get rid of this. And now I can set up a constructor. And in this case, constructor would take the same parameters that our constructor function took before, and we were just passing in the name. And then everything that is in our setup code goes inside of our constructor function. Same way classes work in other places, right? So, we've got a constructor that's going to assign name to this .name, and then I have a toString function, and now I can just list functions this way. So I've got a toString that works, and now I have a class with a constructor and a toString, and this might make everybody really happy, because this looks a little bit more like what you've got in .NET or Java, so I can see the appeal here, where it's like, hey, this looks familiar to me, because then down here below I'm newing up tasks, I'm newing up instances of classes and all those things, and so it feels very familiar. But be careful, because you're really not, and it still has all those complexities we just talked about before, because this toString function right here is not actually on the object, it's on the prototype. And we can tell that, so here, let's run it just so you can see it run, everything's still working, but if we just print out task, a good way to look at an object is just to console.log it after we create it, it's always helpful, you'll see the task just has the name on it, it doesn't have the toString, because toString's on its prototype. And we can tell that it's on the prototype by actually looking at Task., we can actually execute toString off of Tasks' prototype, and it'll say undefined is complete. But there you see I'm running everything off of Tasks' prototype. Now, besides the fact that you can't have a private variable, there's no private variables that exist inside Task, and the only way to expose things to the methods are to expose them through this, which means they're exposed out to the whole world, which may or may not be a problem. There's also one other piece of unexpected behavior that you may run into, in that the prototype is accessible. So, if I can go into Task, so here we've got the two toStrings, I can go Task.prototype.toString = 'hi.' I've just assigned toString to a string called hi. And so, if I print my tasks out again, what do you think is going to print? I have two instances of task, I went back and I changed the prototype on the class, I haven't touched the object at all, and now these two instances are going to break, because toString is not a function anymore, now toString is a string, and that's a problem. So just be aware when we're talking about classes, you don't get a scenario in .NET or C# in Java where at runtime we're going in and we're changing the properties on a class. You can change things on an object all you want to, but you don't generally see changing to classes. But in JavaScript you can, because of the way prototype inheritance works. And so that's just something to keep in mind and something to note that classes in general are stripped down versions of constructor functions, they've removed the ability to have methods just up inside the task and everything's now done on the prototypes. That may or may not detract you from using classes, especially if you're coming from a class-based language like .NET or Java where you expect a class to work a certain way, just know the classes do not work that way here in JavaScript.
Extends and Super
Now before we go too far on the no classes, classes are terrible bandwagon, I'm not quite that far, and let me show you some advantages to classes, and some cool things that you can do with classes that are much simpler to do here than they are using the old way. So, and that's extends and super, and so basically we're talking about inheritance here. And so here I've got a class, and it's got its constructor, it's got its toString, everything that we just talked about, I'm going to fold this class up so we can see everything that's going on. I'm going to create a new class called Urgent, and when I create this Urgent Task, I'm going to say extends Task. Now the only thing I have to do at this point is create a constructor. Now the constructor takes name, just like task did, and I can just do super, pass in name, and be done. Now task2 is an urgent task, and urgent at this point means exactly the same thing that task does. So if I run this, notice that everything's still exactly the same. Now I can start adjusting some things, so since this is an urgent task, I can add exclamation points around it, so now after I fix it, now I get exclamation points around module 2, because it's urgent, right? And that's the simple inheritance that we have around task, and so basically Urgent points to Task's prototype, and all of those types of things that we just talked about with prototypes, but this is a very nice clean implementation of inheritance in JavaScript classes. Now we can take this one step farther and create our own toString, and if you remember the toString on Task just prints out whether or not it's complete, so Task is complete or is not complete. What we want to do, if I fold task back up in toString, is say urgent task, and then the super.toString. Now I'm returning urgent task and then the string, so module 2 when I print that out will say urgent task module 2 is not complete. There we go, urgent task 2 module 2 is not complete. So that's extends. Extends and super and how all that stuff works, so extends is a just very simple, nice implementation of inheritance for our JavaScript classes. So, classes aren't all bad, there's some cool little features to them, and some nice little implementations if they fit what you're trying to do, then the syntax is great.
Generators
Now the last thing we're going to talk about in this module on functions is generators. And generators are a cool little way that we can create a function with multiple end points and that you can return back to kind of over time. So let's take a couple of minutes and look at how these things work and what they can do for us. Now the way we make generators work is essentially just create a function. So we say function, and in this case, we'll just say gen. And we're not going to pass anything in yet, we'll add that here in just a minute. So what generators do is have multiple exit points where you can exit the function and then return back to that point and execute it again. So let's create a couple different exit points for this, and then we can go. And the way you do that is with the yield statement. In a regular function we return, well, in a generator we do a yield, and in this case, we're just going to do a yield of 1. And let's do maybe just one more. Yield of 1 and yield of 2. Now there's only one other thing we have to do for this function in order to say, hey, this is a generator function, and we're going to add an asterisk right there, and that is what denotes this is a generator. And so a lot of times what you'd have is you'd do stuff, you'd yield, then you'd wait, and then you'd do more stuff, and then you'd yield some more. So the way you run a generator is you create a variable. So we'll just call it it for iterator, and that's kind of essentially what this is, and you'll see that as we build through it, but I'm just going call it it for now, and it's going to be gen, and we're going to execute that function. Now that I've executed that function, it doesn't really do anything yet, I actually have to call one more thing to make this work. And what I want to do is I'm just going to print out, it.next, and we execute that. Now when I run this, check this out, I get the value 1 and a done of false. And the 1 came from this yield. So here I did yield 1 and that was my result, and the done false means hey, there's more stuff. So you know what? Let's run this again. And just looking at it should be obvious, the return for the next ______next call is going to be 2, right? So now we'll get a 1 and we'll get a 2. Still a done equals false, and that's because we have two yields. Now, it looks like we're done because, hey, I only have two yields, but we're not actually done until the function exits normally. So let's add one more of these, and now when I run it, I get 1, 2, and then a value of undefined. Done is true. So now the function is executed all the way through, it executed 1 and executed 2, and then it just needed somebody to say hey, now we're done. Okay, now one cool thing about generators is they're going to be two-way conversations. Like, I can initialize my generator by passing in something in here. So let's do that, let's just pass in Init, and we've got a parameter on our function called input. And let's just output our input. So, yield is going to be our input. Alright, now if I delete everything, when you're doing a generator, these first 2 statements, line 10 and 11, always go together, because it doesn't actually do anything until you call it.next. So you create the iterator and then you call next. And so what we're going to get now is our input, right? So value Init, done. So that's the starting point, but now I can do this, var netIn = yield. So, on my next call, I can pass in something. We'll just say NextInput. So when this yield right here gets executed, I exit right here. Now I'm sitting on line 12. Then my next, my it.next, whatever parameters I pass in here, will be added for my yield, and so netIn becomes NextInput. When I call my console.log here, I'm actually going to get this NextInput. Well, I'm not, because I actually have to use it. Boom. There we go, Init, NextInput. So that allows us to have a simple two-way conversation between our function and the execution of the function. And now let's look at something that may be a little bit more complicated that we can do using generators and maybe a repository.
Repository as a Generator
So now I have a book repository, and in this book repository I've got a list of books, and it's just books with various titles. And I'm returning my list when I execute my bookRepo, and that's all cool, but I can use generators to control the flow of books from the repository into my controlling application, and this is just one example of the types of things you might be able to do with a generator. And so let's turn this into a generator, and let's say hey, I want the first two items from my list. And so now we have a parameter being passed in called, let's just say num, let's just call it num, and that's the number of items I want returned. And then we can just loop, we can create an outbound array, and then loop over our list and push the item on until we have as many as we asked for, and then do a yield out. So look at line 22 right here, I'm yielding my out array, so that's my list of books that I'm done with, and then I'm taking another number, so basically starting over. So, I'm overriding num with something new, and then I'm erasing my out array. So, check this out, let's see what this looks like. So, I've got my repo = bookRepo, and then my initial repo.next, and we're going to execute that. Now when I run this, notice I've got two, 'War and Peace' and 'Wind in the Willows' with done equals false. So I got two books back. Now when I run this a second time, and let's say this time I'm going to run it with 4. So hey, I got my first two, now give me four more, and I can control how many I want back. Now when I call repo.next 4, the function has stopped right here on line 22, I'm in a holding pattern right there, and I'm not going to do anything in this function until next is called, and when next is called, this 4 is set to num, and then I overwrite my out, and I start my loop again, I don't start it again, I continue my loop where I left off, except now out is empty. And so now when I do 4, I'm going to get the next 4 books in my list. So, the first time I run it I get my 'War and Peace' and 'Wind in the Willows,' and then I get four more books. So, now when I call this again with four more, now I only have seven total in my list, there's only seven books in this list. So when I call four more, I'm going to start over, four yield out. Well, my if's never get execute, because I'm going to loop over. And so what I want to do down here instead of a return, I'm going to do my final yield, and I'm going to yield out. And now I'm only going to get just a couple items, except i before e, yield, there we go. So I get two the first time, four the second time, one the last time, and done equals false. And so if I executed this again, then I'd get my done equals true. Now this is just a simple example of how I can take a repository or something like that and control the flow of items coming out of that repository with, hey, give me two, hey, give me four, hey, give me two more, give me my next group, and have the repository only loop over and hand back the things that it needs. So one example of where generators can be interesting. Now, you can do this in other ways, this isn't earth-shattering, this isn't something you couldn't accomplish in some other way, especially with closures and saving things outside of your function, but this is just a nice little concise way to get something like this to work.
Summary
Alright, so that's our module on functions. And we started with the conversation about arrow functions, and how the this in arrow functions are lexically bound, which makes the this keyword for callbacks especially. So if I'm doing callbacks involving this, arrow functions save us a lot of heartache, because this is now bound "correctly," correctly in air quotes, so that it does what I expect. So arrow functions are fantastic for those callback areas where I want things to line up the way I expect them. We talked about classes and how classes are not what you're used to, they're a little bit different in some subtle areas that may or may not work for you, and so that's kind of up to you now that you know what classes are doing and kind of how they work, you can make the decision for yourself whether or not classes are an ES6 feature you're going to use or something you're going to kind of pass on. But we also talked about inheritance with classes and the extends keyword and the super keyword and how all of that stuff works, and that can be kind of cool, it's nice little syntactic sugar sitting on top of the way objects work. And then we ended with a conversation on generators, and generators are just kind of this cool neat little thing that allows you to interrupt execution of a function and hand something back to the calling procedure and then maybe get some feedback before you continue and do more things.
Built-ins
Introduction
ES6 comes with some really cool built-in features that make our lives a little easier when dealing with things like arrays or collections of objects, and they also provide a very cool alternative to callbacks. Now there are several of these built-ins listed out on the kangax website, but we're only going to dig into three of them that I think might be the most useful to you in your Node.js development. We're going to start by having a conversation about sets and how sets are an array-like thing that makes sure that each piece in the array is unique and some functionality built around that, and those are pretty cool. We're also going to talk about maps, which is kind of a combination between a JSON object and a JavaScript object and makes our lives a little bit easier so we don't have to jump all the way to a JavaScript object when we don't really need that structure. And we're going to end with a conversation about promises and how promises help us deal with asynchronous code a little bit more cleanly than callbacks do and kind of separate out our concerns a little bit and make our code much more readable when we're dealing with asynchronous code.
Sets
So the first thing we're going to talk about in our built-in module is sets, and sets are kind of an array-like object that stores an ordered list of items. And really it's easiest to understand this once we kind of start playing with it. So let's just build something out, and then we can see what we've got. So, in order to get a set, we're going to do var items = new Set. Now you have to do new when dealing with a set, so this is an object that its own thing, and we create it with the new keyword. So now I've got a new set, and I can add items to my set using add. So, items.add, and I can add something to this. So let's just add, let's just say a 4. And then I can see, I can look into my set with two different keywords, so the first one is, and we'll just console.log this, the first one would be items.size, and this is how we know how big a set is, and in this case, items.size is going to be one, one thing. The other way we look into this is with has, and this is the cool strong part of the set versus just an array or something like that, I can actually say items.has and check to see whether or not this list has a specific item in it. Now when I run this, I get 1, for I have 1 item in my list, and it does have 4. There's a couple things about a set that we want to look at real quick. First and foremost, notice I've stuck a string in there, 4. I could also put a number in there, in this case, let's just do 4. And what you'll notice is that I can do both 4s. A set does not check or does not type an item when you're adding it to the list. So if I have a number 4 and a string 4, it's going to allow both of those things in. When I run that now, notice I've got two items in my list. However, if I try and add 4 again, it's not going to take it. Now I still only have two items. So this is an ordered list, but it's a unique ordered list, so I can only add things that are not already in there. And notice it didn't give me an error, it just didn't do anything with it, which is kind of awesome. Okay, so I can also do this. I can pass in an array of other things when my new and now I'll have the four items from the original, plus the two more, so I'll have six items total. And I can do my item.has and all that kind of stuff. Now, a list that I could only get to with checking to see whether or not an item exists is one thing, and that's kind of useful, but I can also iterate over this, and this is where it starts to become a little bit helpful. So I can do for, let item of items, using our ES6 of loop, cl item. So check this out, now there's my list. My 6 and my true came from before, but then 5, 6, 7, 8, 4 the string and then 4 the integer are all there. Okay, so this is a real quick look at sets. And if you are trying to keep a list of maybe keys for an object or things that you're using here and there, you can add those items into a list or a set and then just check to see if something's already in there. So, if items already has 4, then I'm not going to do something. If my list already does have 4, then I can do something else. Now one last thing to look at is the clear. So I can do an items.clear to empty out my set. There you go. Now everything's empty, because I did my clear right here. Alrightt, so that's just a quick look at sets so we can kind of get an idea of another type of object that we can use if we're trying to keep track of an ordered list of items. Alright, I can't talk about sets without also talking about weak sets, because they kind of have the same name, they both have sets in them, but weak has kind of a weird connotation to it. So I want to talk through a little bit what a weak set is and how that compares to a Set. Now remember, everything that we talked about with a set we used integers and strings and in a weak set we can't do that. Weak sets are only about objects, and so you have to have an object that you're adding into your set. And so here what we've got is a new set, so I'm still with the set for just a minute, and I've got two objects I'm adding, x and y. And then inside my if statement, I have a third object, I let x = baz, and so if you've been paying attention up till now, let is block scoped. So inside our if statement is the only place that this object exists. Outside of this if statement, this object no longer exists. If I add this object to my set, and then I iterate over my set, that object is going to show up inside my list. So if I run this, notice how I still have my baz 1, 2, 3, inside my set, even though the scope of that object no longer exists. And so that's something that a set does that a weak set's going to kind of think. If I go with WeakSet, if you have a list of objects that you're trying to keep track of and just know, hey, does this object exist or not, have I messed with this object yet, WeakSets are perfect for that, because they're going to be truly inside what's in scope and what exists and what doesn't exist. So right now this WeakSet, when I run this, we're going to have one problem, and I'll explain that to you in just a second, but at the end of this, I'll only have two items. So here we go, let's walk through what the WeakSet does. So, var x, var y, it's all added in, at this moment, when I get to line 11, this x baz 123 exists, it's totally in my WeakSet. When I leave scope, that gets garbage collected, and it's gone from my WeakSet. Now there's a limitation to my WeakSet, because I can no longer look at the size of the WeakSet, and I can't iterate over my WeakSet, because it's only kind of loosely coupled, like things are only loosely in the set, and it keeps track of them only kind of, so that it allows for garbage collection, I mean, that's the big piece of this whole thing is for it to allow garbage collection. So I can't do this, I can't iterate over it anymore, all I can do is see if an item has, so let's do cl items.has. And in this case, we'll do y just so you can kind of see how this works. So notice I do have, so line 11, I do have x in there for baz 123, but there's no way for me to even get to this baz 123 anymore after I leave my if, because that x is no longer in scope. So WeakSets are kind of that thing where if you need them, and you have memory issues around keeping track of objects in a set, WeakSets are exactly what you want. Other than that, you're probably never going to use them. But I wanted to take a minute and talk about the difference between sets and WeakSets, just so we're all on the same page.
Maps
So the next built-in type we're going to talk about are maps. And maps are a little easier to understand if you just think about it this way. Maps are essentially just dictionaries. Think about them kind of like JSON objects, right? You've got a string, a colon, and a string, or something else. The difference really for maps, though, is that anything can be on the left side of the colon. You can have an integer there, or whatever you want there, and typically in JavaScript right now when we do something like this, we're using an object. So if you look through everywhere in any of the code we've written through any of these courses, when I have something like this, I'm creating an object literal, and I've got something on the left side and something on the right side. The difference between an object literal and a map is that they must be strings on the left side of the colon, it has to be, because it assumes it is and makes it into a string. So let's talk about maps. Let me just kind of show you maps and then you can decide for yourself if the advantages of maps are worth it to you to stop using object literals for this type of thing and start using maps. So to do exactly what we just did, we're going to create a person that equals a new map, and then what we're going to do is we're going to start adding parameters to this map, and so this is where it becomes a little more cumbersome than using an object, but stick with me. So, I'm going to add those three parameters using person.set, and I'm going to set a name and I'm going to set the age and I'm going to set whether or not I'm single into my map, and now I have in my map almost the same thing as what you would expect out of an object literal. In fact, if I do a console.log on person, what you'll see is almost exactly that. Now, don't get freaked out by the arrow instead of the colon and all of that, but at its basic, that's kind of what we've got. There's a couple of things that a map can do for me that an object can't, or can't directly. Yes, there's ways to do this, but bear with me. I can access the map size, so person.size directly. Boom. Three, instead of having to do person.keys.length and all that kind of stuff. So, access to the map itself is pretty easy. I can also do person.get age. And that will print out that I'm old, because I am. And what you see is this is, I get what they're doing, but it's not quite as simple as the object notation, and that's okay, don't let my, I'm showing my bias a little bit, but there are reasons why map becomes very cool, and there's reasons why it doesn't. So, just kind of work through that on your own as you think about this, and whether or not maps start to make sense. Here's one cool thing. If this was an object, and I wanted to check to see if single existed, so I would do if, let's just pretend this is an object, I'd do person.single. Well, I can't do that, because single is a falsey value. So if I wanted to do person.name, we'd just kind of do that. We'd do that as a matter of course all the time. Well, I can't, because person.single is a falsey value, because it's false, but it exists, and so we end up having to do type ofs and things like that. But what a map allows me to do is I have person.has, and I can say, hey, does person have a key called single. There's a couple of other ways we can do this, interact with our map. We can send in a series of arrays or not just a series of arrays, but an array of arrays, where I could say, hey, town and kc, or kids and 3, and now you can start to see, oh, not colon 3, force of habit, there you go. Now I've got five items. And if I just log person again, there you go, now I have all of that stuff associated with my map. Okay, so why use a map? What advantages does map have over just an object literal? And there's a couple. First of all, this is ordered. A map is ordered, the order that things go in or the order they come back out again. Objects are not ordered, so that's one nice advantage of that. The other advantage is that it's easily iterable, which is a little bit, I mean you can argue that yeah, you can iterate over the keys of an object and all that, too, but I can just do person., let's get rid of my if, person.forEach, and iterate over my person just like that. I can person.forEach, boom. There we go. So, that's a map, I mean at its core that's kind of just what it is. And the syntax to me still feels a little bit clunky as opposed to just interacting with objects, but maps are smaller. They don't have the prototype associated with the heft of an object, and that may or may not matter to you, that's going to be kind of up to you to see what you want to do and what works best for you. Now, WeakMaps are going to be almost identical to what WeakSets were, except that it's a map, so you've got the key value, but again, on a WeakMap, you have to have an object as your key, and then you can't iterate over it, you can only access it as it goes. And so I'm not going to spend a lot of time walking through that, it's exactly the same thing as Set, just with a Map instead of a Set. And again, pick where you want to use it, and where it makes sense, but that's a memory safe way to work with maps.
Promises
Now I'm going to take a quick look at promises, and promises are something that have been around for awhile in different promise frameworks, but now they're baked into ES6 natively, and so with Node, we can go ahead and start using those without having to worry about using a promise framework outside of just native JavaScript. Alright, promises really at their core just help us a little bit with an issue with separation of concerns. And so typically when we're calling an asynchronous function of some type, we have arguments, which we're passing into the function, and we have our callback function, which is coming back out of our async function, and we have everything kind of muddled together there inside our async function call. And actually sometimes like with set timeout, the callback's kind of like buried in the middle, which is weird, and sometimes it's hard to know, especially when you're using named functions, where everything goes. And so what promises do is they make functions vettable, and what that means is that they separate incoming from outgoing arguments, so you have the arguments in the function call, then you execute a callback function as a separate thing, it just pulls those two things apart, and at its core, that really is what a promise is all about. So let's take just a second to look at what that looks like kind of in practice. So here we've got some code that is going to connect to a MongoDB database, and then we're going to go find a book. And what happens here is you can see our mongodb.connect takes in a URL, and then we've got our function that takes either the database or an error. If we have an error, then we're going to send an error, and if we don't have an error, then we're going to go find a book. Now let's look at how promises start to let us pull apart all these different pieces into separate things so we can keep better track of what's going on here. Now, for the sake of discussion just for right now, let's go ahead and pull this function out into a named function, just so that you can track a little bit better what's going on so that we are not mixing up one thing with another. So we'll go ahead and do that, we'll pull that out, and then we'll create it in its own separate function, and then I am going to get the book. So now this helps a little bit, it kind of clean up everything that's going on, but we still don't have that nice separation of concerns, and so what we do is I can cut this out, close it, and do a .then to open it. So now it's very clear to see, hey, m mongodb.connect with a URL and then we're going to go get the book. And that's cool, and that's a little bit cleaner and easier to tell what goes where, and ultimately that's kind of what promises are all about. But then promises go on to try and fix one more issue. And that comes from this. Usually our async functions look more like this. They're anonymous functions, which is part of it, but then they also have errors and results in the same function block. And so this is kind of back to the separation of concerns conversation, where we have a function, one function, that checks errors that are coming in, and then goes ahead to process the results, and those are two very different functions that need to occur. And so the other thing that promises do for us is they allow us to do two different callback functions. They have success and they have error, and this is standard by default, this is just kind of the way it works. So, instead of just passing in, hey, here's my callback, we can now pass in two different functions that deal with two very different things. So let's kind of look at what that looks like. So down here you see I'm still passing in error and database into my getBook, and that's not how this works anymore. So we're going to drop off our error. And now we're just getting our database. But we no longer have to check to see if an error happened, because we're going to do that separately. We're just going to pull that out, and say hey, if getBook is executed, then I've got a database and I'm ready to go and actually do what this function is built for, which is getting a book. And I can fold that up, and I can add another function error. That's actually going to get my error, and then inside my function I can put that if err block, and really, if this is being executed, I don't really even need to check for if error anymore, so now I just have this one little, hey, if I've got an issue, we're just going to send a 500 error and be done. And then up here in my then, I say mongodb.connect to a URL, then we're going to get a book, or we're call our error function, one or the other. That's how promises work, that's how consuming promises work, and that's really all there is to it when you get right down to it. Now real quick on the other side when we are creating a promise, it's just a promise, you just create a new thing. So let's look at that real quick. Let's go down into our goodreadsService down here, and right now I've got a showBook, a function, an id, and a callback. So if I want to do this as a promise, I just drop the callback and say, hey, we're not doing the callback thing anymore. And then we're actually going to return a new Promise, because all a promise is is just an object, it's just a thing, right? And we're going to return a new thing called a promise, and we're going to pass into this new thing a function, and this function gets executed with either fulfill or reject. Fulfill and reject are two functions that get called that may actually even look, if you go back over to our book controller, hey look, getBook and error. When I fulfill, I'm calling getBook, when I reject, I'm calling error. That's just kind of how that works. Okay, so now inside this function, I can take everything else that I'm doing, all of this, and just drop it inside my function, and that is all it is. And now wherever I was calling my callback, it's just down here at cb result.Goodreads book, I can just call fulfill instead. And wherever I was calling my error, so like right here, I can say if err, we're going to reject with error. Now, calling fulfill and reject don't interrupt execution of the function. So, really usually a good idea when you're calling fulfill or reject is actually just do a return. And we're going to do a return there, too. Alright, so that's promises, that's a very quick look at what promises look like when we're dealing with promises, and if this is something that's now interesting to you, if you kind of like this separation of concerns that goes on, dig into promises a little bit more. There's several other courses out there that cover promises much more in depth, but really all I wanted to do with this is just to kind of peak your interest and say, hey, look, my mongodb.connect looks a lot better when I, one, get rid of my anonymous function, but also when I start separating everything out so that it looks a little bit cleaner so you know what's going in and what's coming out and what happens when an error occurs.
Summary
So really that's it. That's the vast majority of the stuff that's going to be available for you, especially in the Node environment for ES6. And remember, it's just a set of cool new features, and we talked about a lot of stuff over this course, and there's a lot of things that you can pick up and use right now in your applications, especially things like promises or template literals or things like that, arrow functions especially to fix our this problem, but you can just pick and choose. There's lots to pick from, but you don't have to use them all. Pick the ones that make sense for you, and start playing around with those, and remember kangax is your friend. There's a lot of other things out there that we didn't really talk about, and mostly because they're not going to be as relevant to you necessarily in the Node environment, but kangax is your friend. Go out there, check out kangax, and see if there's anything else that you might want to pick out and play with, and remember kangax also shows implementation, just kind of hover over the question mark, and that'll get you done. Thanks a lot for hanging out with us over the course of this course. If you have any questions, don't forget to drop down to the discussion and we can check you out there. And we'll talk and answer questions down in discussion. Thanks a lot.
Course author
Jonathan Mills
Jonathan Mills is a JavaScript and Node.js expert working mostly in the MEAN Stack with individuals and companies to help build their technical skills to cope with the constantly changing landscape...
Course info
LevelIntermediate
Rating
(62)
My rating
Duration1h 44m
Released10 Aug 2016
Share course