What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
JavaScript Fundamentals for ES6
by Joe Eames and Scott Allen
This course covers the features of ECMAScript 6, the next version of JavaScript, and how you can use them today.
Start CourseBookmarkAdd to Channel
Table of contents
Description
Transcript
Exercise files
Discussion
Learning Check
Recommended
Introduction
Introduction
Hi, this is Scott Allen, and this course is all about the new features added to the JavaScript language by the ECMAScript 6 standard, the ES6 specification as we call it. It's been a long time coming for JavaScript, because the language has been hampered by its own success. Not only is JavaScript the ubiquitous language for the web, it's reached beyond the web. It now runs inside of database engines, web servers, game consoles, and refrigerators. Backward compatibility is important when there is so much code depending on a language, but ES6 is finally here to move the language forward and it's adding some features that we've come to love in other languages, as well as some fixes and some capabilities unique to JavaScript itself. This course will cover all that's new for ES6, so let's get started by taking an overview of what ES6 is all about, when and where we can use ES6, and some of the tools you can use to try out ES6 today.
ES6 The Specification
JavaScript is an implementation of ECMA script, and the ES6 specification that defines the language is, as of this recording, still in draft status. It's not expected to be released until June of the year 2015, however, the features are frozen and we are in a period of validation, where developers who build JavaScript engines and developers who write JavaScript code, we all have a chance to work with the new version of the language and provide feedback. That should result in a clean specification that is free of ambiguities. We have the ability to work with this new version of JavaScript already because the feature set has solidified well, and it's been worked on for years, and many runtimes, browsers, and tools are already building out the new features. What's taking so long is that ES6 introduces an extensive amount of new syntax to the language. This is the biggest addition to JavaScript since JavaScript was created. The last time the specification tried to add this broadly to the language was with ECMAScript 4, but that project was abandoned. ES6, however, which started with the code name Harmony, it is here, it's agreed upon, all we need is the formality of an official release. Let's get an overview of what's new inside.
What's in the Course?
Joe and I designed this course to break down the features of ECMAScript 6 into a few different categories. The next module of this course covers the first category, which is working with variables and function parameters. These are features that can change your every day programming style. We'll be looking at features like the new let statement. Let gives us true block-scoped variables in JavaScript. Let will be the new var. It's safer and it allows us to declare variables close to the code, using those variables. We'll also look at rest parameters, default parameters, destructuring assignments, and more. And then the next module will introduce you to the class syntax of ES6. We've always had the ability to create objects in JavaScript, objects with properties and methods, but this new class keyword will allow us to do that in a familiar way, familiar if you've ever programmed with a language like C++, C#, Java, Python, Ruby. In all these languages, a class defines a blueprint for constructing objects, and it includes the pieces that we need to define the state, define the construction logic, even set up inheritance relationships between objects. And then after classes, we will turn our attention to some new functional programming capabilities in JavaScript. JavaScript has always been a very functional language, but one new bit of syntax that I'm very fond of is the arrow function. An arrow function allows me to create a function using a terse syntax. There's no function keyword required, and in fact, for many scenarios you don't need a return statement or curly braces either. We'll also be looking at iterators, generator functions that can create iterators, as well as the new list comprehension syntax. From there, Joe is going to take you on a tour of some new built-in objects for ECMAScript 6 like the Set and Map collections. These are new data structures we can use in JavaScript and they have memory-friendly counterparts, the WeakMap and the WeakSet. Joe will show you how to use these collections, and also go over new improvements and APIs for objects, arrays, numbers, and more. Then Joe will look at asynchronous programming with ES6. Asynch programming has evolved over the years to favor Promises, where a Promise is an object that will promise to give you the result of some asynch operation in the future. There have been unofficial standards for the Promise API, but now ES6 has formalized an API. Joe will demonstrate how this works, as well as show some other very interesting asynch programming techniques we can use when writing ES6 code. After asynchronous programming, Joe will go on to describe some of the other built-in capabilities of every ES6 object. There's new APIs, there's new methods available, there's also new meta-programming capabilities available through objects like the proxy object. And then in the last module covering new ES6 features, I will look at modules in JavaScript. JavaScript has been sorely lacking in module system, because modules facilitate writing isolated independent bits of module code, that's something that we need these days for complex apps. Again, this is an area where unofficial community-based standards have been in place, standards like common js modules and the asynchronous module definition, the AMD standard. We'll look at the ES6 import and export syntax, and also look at tools to make ES6 modules work with existing standards like AMD. And finally, one module you might want to watch early on if you want to follow along with all the examples in this course, is the module about using ES6 today. There are a few projects and teams who are already writing ES6 code for production at this early point, and they use tools like traceur, which can transpile ES6 code into ES5 code, so we can use the new syntax and runtime features of ES6 when we write the code, but we will produce code that can execute in all the browsers and devices that exist today. Joe will cover lots of tools and how to set up these tools, so feel free to skip ahead to this module featuring tools if you want to get set up with a sturdy environment for writing ES6 code. I also want to take a little bit of time in this introduction just to give you a taste of writing some ES6 code. Let's try that next.
Try It Yourself
One easy way to try out some of the features of ES6 is to use a tool named traceur. Traceur is the name of a Google project, it gives me a JavaScript library that I can run in a browser. There's also a command line traceur tool I can use from a shell to compile code during the build process. I will show you how to use traceur in a browser, Joe will show you how to use some of the other features at the end of this course. And I said that Traceur allows me to use some of the new ES6 features. We'll discuss exactly which features are available in the next section. For now what I want to do is use traceur in a Plunk. A plunk is something I can create on the Plunker website, plnkr.co. This site is a really easy online development environment for JavaScript. Once I get the plnkr.co, I can click Launch the Editor, and on the left-hand side of my screen is my project. These are the files in my project. We'll just be using the html file. In the middle of the screen is my code editor, and on the right-hand side of the screen I can toggle between various options by clicking on these icons. There's a live preview so that as I edit code inside of here, it just automatically updates, I don't have to click a Save button, and if I do want to save this project I can. That means I can bookmark it and come back to it later. For right now, though, I want to go back to this tab, which is find external libraries. I want to search for traceur, there's my one result. I can click on this little icon right after the name, and what Plunker will do is add script references into my html file. Let me close this third pane, just so I can show you that we have references to traceur-compiler.googlecode.com. One is traceur.js. That is a file full of JavaScript code that knows how to parse ES syntax and convert ES6 into ES5, and then there's a bootstrap.js file. This contains just a single line of code to instantiate traceur and tell it to run on this web page. So once I have those two files in place, I can go out and create script tags and write ES6 code inside, but they have to be of type "module". That's because I don't want the browser to execute the code that is inside of here, I want traceur to be able to find this, and it's going to go out and look for script type="module", examine what's inside, and do what we call a transpile, that is compile ES6 code into ES5 code so it runs even in a browser that doesn't understand a lot of things about ES6. So let's create a function to add two numbers together. I could do that using the function keyword or I could do that using a syntax we'll look at later in the course, which is the arrow function. The arrow function looks like this. I can say, I need two parameters x and y, and given those two parameters, I just want to return x + y, and now I can do a document.write, the result of adding together 5 and 3, and that gives me an 8 on the screen. What if I wanted to use the new let keyword of ES6? Well, in that case I'm back to displaying just the content of the h1 tag, Hello Plunker. That means there must have been an error in my JavaScript and if I open up the developer tools I can see unexpected token let, and that's because currently, at the time of this recording, let is considered one of the experimental features of traceur, but I can enable that just by adding a little additional script right here, just after I include traceur.js. I can set some options on traceur, I can say traceur.options.experimental, set that flag to true, and now my script is back to running again. So I can write some code using some of the new ES6 syntax and features inside of a script type="module", traceur will make sure that runs in the browser. What features does it support? Let's look at that next.
Compatibility
If you want to know what platform or browser supports which features of ECMAScript 6, one great place to go is the ECMAScript 6 compatibility table. You should be able to find this just by typing ES6 compatibility table into your favorite search engine, because it should show you about kangax.github.io. On this web page on the left-hand side of this table, is a list of ECMAScript 6 language or runtime features, and moving across the columns to the right are various browsers. We have Internet Explorer, Firefox, Chrome, Safari, WebKit, Opera, and then on the far right we have the platforms like Rhino and Node. We also have Traceur. And one thing you'll notice is that there's no single platform that supports all of the features, although Traceur is pretty good, and with some additional libraries or polyfills, you can get pretty close to 100%, but this does mean that throughout this course we'll need to use a different combination of tools and libraries, and sometimes we'll need to use Alpha versions of a piece of software or turn on some flags to enable experimental features, more about that in the next section. Another interesting piece that I'll point out is if you hover over the gray icons here, you can see the tests that are being used to see if a feature exists in my current browser. Most of the tests consist of using eval against a piece of JavaScript code that contains the new feature, and testing to see if eval throws an exception. This table is constantly updated, so you will be able to use this to determine what features of ES6 you might want to start adopting, what browsers or platforms you'll need to watch out for, or polyfill, and just by taking a step back and looking at the overall colors of red and green, it will give you a pretty good idea of where we are in the ES6 adoption cycle.
Getting the Latest
Sometimes while we were making this course, Joe and I had to turn to the latest version of a browser or piece of software, and by latest version I don't mean the latest stable version, I mean the latest beta version or alpha version or even a nightly build version, so for instance, with Mozilla Firefox, that browser is being aggressively updated with new ECMAScript 6 features, but most of those features are still in the Firefox Nightly build. If this is something you want to try out, just do a search for nightly build of Firefox, and Google can and even find that when I don't type Firefox correctly, there's a web page nightly.mozilla.org. Here you can find links to get the Nightly Firefox for Windows, for Mac, for Linux, for Android, and down here at the very bottom of the page you can also look for other nightly builds from previous dates. So if you wanted to go back to the nightly build for a specific version of Firefox or from a specific date, you can find those on a link from this page, nightly.mozilla.org. Chrome does this a little bit differently. Chrome uses release channels, so if I do a search for chrome release channels, I should be able to get to a link on chromium.org that will point me to the Stable release, the Beta release, and the Dev release. These are all usually one version apart. So if install the Stable release of Chrome today, I would get version 36, the Beta release, currently 37, and the Dev channel release is version 38. Then there's the Canary build, which would be very similar to a Nightly build. It might not be completely tested, it might break down completely, but it will also have all of the very latest features that are being built into Chrome. And one other thing to be aware of with Chrome is once you have it installed, you can sometimes enable some additional features in JavaScript just by setting some flags. You can get to the flags by going to chrome://flags, that will bring me to a page where if I do a search for JavaScript I should be able to find this flag, enable experimental JavaScript. This is disabled by default, but I've already come in here and clicked on the Enable link for this particular flag that gives me some additional ECMAScript 6 features. And I can try some of those features in the developer tools console window. And remember that as the last module of this course, Joe's going to go into some details about Tools, so if you want to get things working and follow along with a lot of the examples that we do throughout the course, you might want to do the Tools module earlier rather than later. Also, you might need some help setting up a project, an environment, getting all the files in the right place. Let me show you a way you can do that next.
The Repository
If you are looking for help in getting set up with an ES6 development environment, including a server, a test runner, a live reload of the browser, then Joe and I are setting up and maintaining a Github repository for the course. The repository does not contain the exercise files for the course, but it will include a number of starter projects you can use to write code with different ES6 features. The repo is currently empty, but we will update this repository with the setups we use for the course, and then keep the repo updated as tools and capabilities change. What you'll see in here when you visit at a future point in time is a number of different folders for different scenarios so you can get up and running quickly. These setups will require you to use nodejs. Fortunately, nodejs does play well with other development platforms, so even if you wanted to use a Windows Tool like Visual Studio as an editor, nodejs will work alongside Visual Studio, it will run a web server and compiler in the background for you, and the install is easy, just go to nodejs.org and click the big green INSTALL button.
Get Started!
Now that you have all the meta-information about what the course is about, what modules are here, and where to get some information and help, let's get started with the course. Our first topic will be new features for variable declarations, assignments, and function parameters.
Variables and Parameters
Introduction
Hi, this is Scott Allen, and this module is about new features in ECMAScript 6 for variables and parameters. These features were designed to make JavaScript code easier to read, easier to write, and also to make the code safer, because a few of these features were designed to avoid some of the strange and dangerous behaviors of JavaScript. We'll be looking at features like the two new keywords for declaring variables, that is let and const, as well as how to assign default parameters, use rest parameters, and much more in this module on working with variables and parameters.
let
The first new feature we'll look at is a new keyword in JavaScript, the let keyword. Let allows us to define variables. We've always had the ability to define variables in JavaScript, and we've done this with the var keyword, however, the var keyword has some limitations when it comes to scope. The scope of a variable defines the area of a program where the variable is legal to use, but with var there are only two types of scope for a variable. There's global scope, which is where I would place a variable if I defined the variable with var outside of any function, and then there is function scope for variables defined inside a function. And there is no block scope, and this is a source of confusion and occasionally bugs, particularly if you come from a background of using one of the other many languages that use curly braces to define blocks of code. It looks like I have a variable defined as x that lives inside of the if statement, but in reality, because var can only give me function-scoped variables, x is available throughout that function, and the return keyword is going to work perfectly well, because there is going to be a variable named x inside of doWork, and that is the problem that let will solve. the let keyword will give us true block scoping. The second code snippet will generate an error if there's no other x variables defined in an outer scope. There will be an error, because x is only available to use inside of the if block. Let's take a closer look. Here inside the code window, I have a Jasmine test set up. It contains some of the same code that we saw on the slide, because there's a doWork function and inside of an if statement of that function, I have var x equals to 3. If I invoke that function and pass in a value of true, I would expect that the result that function returns, I expect it will be a 3, and if I save this test, the browser will automatically refresh and I can see the test is still passing. That is indeed what happened, because x is available throughout the doWork function, and it's perfectly legal to say return x at the bottom of that function. But what happens if I pass a false flag and we never execute that line of code that says var x = 3? Well now if I save the test, it is, of course, going to fail, but it's going to fail because the assertion failed. I did get a result back from doWork, it just so happens that that result is undefined, and that behavior is significant, because one of the confusing things about the var keyword is that variables are automatically hoisted up to the top of a function regardless of where I actually had the var keyword. In other words, it's as if at the top of this function I said var x =, and then gave it the value undefined. That's how you can think of it behind the scenes. When I have a line in here like var x = 3, it's as if x is available throughout that function, but it's only going to get the value 3, if that flag is true, and that's completely different from how let works. Instead of using var, I will use the keyword let. I'm still passing a flag equal to false, so we're not going to execute that line of code, and now if I save it, notice the error has changed, so there's now a reference error. A reference error occurs in the JavaScript runtime when I'm trying to use a variable that is not available. So return x is now an illegal statement, there is no x, and this is true even if I pass in a value of true, which will create that variable and assign a 3 to it. That's still not working, because x is only available inside of that if statement. If I wanted that statement to work, I would have to place my return statement inside, and now if I save the file, the test refreshes and we have something that's legal. So moving forward, when we are all programming with this new version of JavaScript, it will be a good idea to use the let keyword instead of the var keyword to avoid this implicit variable hoisting and some of the confusing behavior that can produce. Now this block-scoping also works for a for statement. So I copied and pasted my test, and let's, inside of doWork write a for statement. So for var i = 0; i< 10; increment i each time, and then at the bottom of doWork, return an i. I don't need this flag anymore. If I invoke this function of doWork, I would expect the result that comes out of this to be a 10, and if I save the test I can see that test passes also, because now what's happening when I have var i = 0, it's not scoped to that for statement, it's scoped to the function, and when it exits the for loop, it's going to have the value 10, and that's what the return statement will give me. If I change this to a let and save the test, I'm back to having a reference error, i is not defined, so I can see that the scope of the variable i is constrained to the block of code with this for statement. In the future, then, let will be the replacement for var, because it will allow us to declare variables where we need them, it will avoid hoisting, and it will give us true block-scoping, which is what most developers expect.
const
The const keyword is another addition in ECMAScript 6. This one can be a bit confusing depending on the tools you are using, and only because const has been around. Not as part of any official ECMAScript language specification, but the V8 engine has recognized a const keyword for some time, so you might have seen it used in Chrome or Firefox. But the idea is, as the name suggests, to create and initialize a read-only variable, a variable that will hold a constant value and something that you can never change. In ECMAScript 6, const will have block-scoping, just like the let keyword, and let's take a look at this in a test. Inside my test I'm using const to declare a variable MAX_SIZE, setting its value to 10. I expect MAX_SIZE to be 10, and of course, this test currently passes, but I'm also not trying to assign to MAX_SIZE. Let me uncomment this line of code and save the file, and now you'll see that my test doesn't run, in fact, I have an uncaught SyntaxError, Assignment to constant variable. And this represents the semantics of the true ES6 const. There will be an error if you try to assign to a const, and that's a little bit different than the const that has been around in browsers like Chrome or Firefox. What they would do is allow you to assign to a const, but effectively ignore the value. They wouldn't throw an error, but they also wouldn't change the value with that variable. In ECMAScript 6, it is a syntax error. And I want to demonstrate a couple things now. I want to demonstrate that const does have block semantics, and I also want to show you a little bit about how let and const work when you have multiple variables with the same name. Currently, this test has two lines of code inside of doWork that declare variable x, and this is perfectly legal JavaScript. If I save this file, I have a passing test because the value that is returned from doWork is 10. With var, the last line of code to use var wins, so x is equal to 10, but what if I were to change x to a const, and now save this. The error might be a little bit different than what you expect. I do not get a SyntaxError that I'm trying to change a constant. Instead, I'm getting a SyntaxError that variable x has already been declared. And that's another difference between let and const versus var, because with let and const I cannot have two variables inside of the same scope that have duplicate names. If I change that to a let instead of a const, I get the same error, Variable "x" has already been declared. But what if I move this line outside of doWork and save the file? Now my test passes. That's because the x that is defined inside of doWork is hiding or shadows the x that is defined outside of that function. And inside of doWork I'm working with the variable x that holds the value 10, but outside of doWork, I can be working with a variable x that holds the value 12, and so when I'm assigning to x inside of doWork, I'm not writing into that outer x. In fact, I could make it const. Now this only works because const x = 12 is defined in a different block, a different scope, and just so that you can see that const has block-scoping, if I wrap this inside of an if block and save the file, I now get the error that x is undefined. So I would need to undo that and leave x in the function block if I want to be able to use it in the assert down here at the bottom of the test. And finally, that inner x would also work if it was a let.
Destructuring
When we declare variables with var, let, and const, we typically also want to initialize the variable by assigning a value, and one of the new features of ES6 is the ability to use a destructuring assignment. Destructuring is an operation you might have seen in other languages like Python or Ruby. What the destructuring operation allows you to do is assign values to a set of variables by destructuring, or literally tearing apart and pattern matching a complex data structure like an array or an object full of properties. This sounds complicated, but if you've never destructured before, I think you'll find it intuitive after a little practice. For example, the code on the screen is declaring the variables x and y, and assigning the values 2 and 3 respectively. Then in the middle we use a destructuring assignment to say, give the variables x and y the values y and x. I'm not creating a new array, I'm going to remove the 3 from the y into the x and the 2 from the x into the y. The end result is that we've swapped those values in x and y, and this is one example of destructuring, but there's a lot more we can do, let's take a look. Inside of a passing test, I have code that is just like the code that we saw in the slide, it's creating two variables, x and y, and then swapping the values that are in those variables using a destructuring assignment. It's very important to understand that what we see on the right-hand side of this assignment is an array, it's an array built with the values that are in y and x. But what we see on the left-hand side of this assignment is not an array, it looks like I'm building an array literal, but really I'm working with individual variables x and y, and I'm surrounding them with square brackets because I am destructuring an array. In other words, I'm telling JavaScript to take the first value of that array and put it into x, and take the second value of that array and put it into y. Later on, we'll be destructuring objects. If I was destructuring an object with this assignment, I would surround those variables with curly braces. It looks like I'm building an object literal, but I'm really just building an assignment statement and I want to pick off specific properties of some object, but right now we're just working with arrays, so I have the square bracket syntax. And it's a little more obvious that I'm working with individual variables if I get rid of those two lines of code that explicitly create x and y, and instead use the let statement here to say, let there be a variable x that will be initialized to have the first value in some array, and let there be a variable y that takes the second value. Since I no longer have those variables available here, I will just put in literal numbers, and if I save that file, the test still passes. I could even have this array be returned from a function, so if I had a function called doWork that returned an array, I could invoke that function and destructure the result into my variables x and y, which is what should happen now if I save the test, it refreshes and still passes. Now what if this array contained an additional 1 at the beginning of the array? Well, in this case, if I save the test it's going to start failing, because now x is always taking that first value, it's going to get the value 1, and if that's not what I want, if I want x to take the second value and y to take the third value, inside of this destructuring assignment I can always use a comma with no symbol there in any position where I just want to skip some value. So now I want x to take the second value from that array. If I save the test, were back to working again. I could use that empty comma at the beginning, at the end, anywhere in the middle. Along those same lines, what would happen if I was asking for an additional variable z, that's trying to get to a member of that array that doesn't exist, that's not returned by doWork, because it only returns 1, 3, and 2. What would I expect z to be? Well, generally, in JavaScript, if you try to get to something that is not there, like the return value of a function that doesn't return something or the value of a function parameter that wasn't passed, you'll get undefined, and that's also what happens in this case. When I save the test, it's still passing, because z will get an undefined value. And that's how array destructuring works. Let's also look at a similar scenario where I have a function called doWork, but this time, instead of returning an array it's returning an object with first name, last name, and twitter properties. Let's say I wanted to take those values from the object and put them into individual variables, so I want a first name variable and a twitter variable. I can do that with curly braces, it looks like I'm building an object literal, but I'm really not, and the syntax here can be a little bit confusing. Let me just try to format this so everything is appearing on the screen, and I'll write an assert to show you that I now have a first name variable, and that variable will have the value "Scott". If I save this test, we now have a second passing test. Now what can be a little bit confusing about this destructuring is, again, it looks like I'm building an object literal, I'm not, I'm defining individual variables and assigning them values from the object that has returned from doWork, because it's an object, that's why the curly braces are here. And in the syntax that we're looking at, the way to think about this is to say, I'm going to take the first name property from the object returned by doWork, and I'm going to put it into a variable called first name. In other words, what is on the right-hand side here, the right-hand side of the colon, that is defining my variable. If I change this to first, instead of first name, and I save the test, I now have a failing test because first name is not defined in this expect. What I created was a variable called first, and if I fix that, the test is passing again, and if I were to misname this, let's say I forget the e, now I'm looking for a property called firstNam, that doesn't exit on this object, so if I save the test, my variable first will get an undefined value that's undefined instead of "Scott". I need to have that e there so it matches that property name, and save the test, and this is working. You can also drill into complex objects this way, so let's say instead of having Twitter as a top level property, I have a property called Handles, and inside of Handles there might be several things, there might be Twitter, Facebook, LinkedIn. And now what I want to be able to do is create a variable named Twitter, but have it take the value of the object returned from doWork, the value of handles.twitter, effectively. If that's the case, the syntax would look like this, handles:, and then _____ an expression to navigate to the twitter property and put that in a variable called twitter. So when I'm writing this, quite often I'm thinking about the reverse of how I would think about creating an object literal, because the left-hand side of the colon is effectively describing how I navigate into this object, and the right-hand side is defining my variable. Let's just make sure that this works by writing an assert. I should be able to say that Twitter is going to be "OdeToCode", and that test is still passing. Now there is a shortcut syntax. If you are happy using a variable name that is the same as the property name that you're trying to retrieve, you don't have to explicitly specify the variable name, I could just say firstName, and now what I will get is a variable called firstName that takes on that firstName value. I just saved the test, it's still passing. I could do the same thing with twitter, no need to duplicate that. This bit of code is saying, navigate into the object on the handles property, retrieve the twitter property, put that into a variable named twitter, save that test, it's still passing. And so this object destructuring can be quite effective at deconstructing and pattern matching against objects that are being passed around in a system. It also works, by the way, when you are defining a function. So let me pull up this function so it's visible on the screen, and uncomment it. What I'm trying to do inside of this test is I'm trying to invoke a function called doWork. I'm passing it some sort of string parameter, and then a complex parameter that involves data and caching. What I'm trying to simulate here is something like the jQuery ajax call where you pass in a url, what is the URL that I want to call, and then some sort of complex configuration object that can specify things like cookies and http headers and accept types. The traditional way to write that in JavaScript would be to create something called doWork, which is a function that will take a url object and a config object, and then what I would do with that config object is look into things like config.data and config.cache. When I'm writing this function now, I can use this destructuring syntax to say take the object that is being passed in and create two variables for me, data and cache, and give me those values from that object that is being passed in. So if I return data from here, that variable should have gotten the value from the data property of the object that is passed in. Data should be "test", cache should be false, if I had something else here, like a collection of http headers, that would come in as undefined. But if I save this test, I now have three passing tests, because doWork has used this destructuring syntax to create an argument named data that grabs that value from the object, and I can use that directly instead of going through config.data.
Default Parameters
In JavaScript, if a function takes two parameters, we've always had the ability to invoke that function and pass in two parameters or pass in one parameter or no parameters or three parameters if we wanted to. But in cases where we invoke that function and do not pass enough parameters, the author of that function might want to specify a default value instead of working with undefined. Generally, we've solved this problem using expressions like you see on the screen, if the name parameter to doWork arrives to the function as falsey because the caller didn't pass a value, then we will or the value with "Scott" to return Scott in those cases where name is falsey, and falsey includes when the name is undefined because the caller didn't pass a value, as well as those situations where the caller explicitly passes undefined or null or an empty string. With ECMAScript 6 we can have an expression with our parameter, and that expression can provide a value if the parameter value is undefined. So if a caller invokes this version of doWork and does not pass a value for name, name will point to a string "Scott". This code is a little more intention revealing, it's easier to look at the method's signature and see what the default values are going to be, and it's generally all-around better than writing these or expressions all throughout the function. Let's try it in some unit tests. Here in my test I have a doWork function that looks just like we had on that slide. If someone invokes this function and does not pass an argument for name, it will assign the string value "Scott" to name. And inside the test, I'm invoking that function without passing any arguments. I expect the returned result to be "Scott", and this is a passing test, so this works. But I do want to point out some important differences in behavior between this default parameter syntax and what we used to do in JavaScript, which would be something like name = name or, and then some default value. This line of code will assign the value Scott, to name, anytime name is falsey, and that means if I invoke doWork and passed in an empty string, that line of code that I just deleted would assign "Scott", but that doesn't happen with the default parameter syntax, it only works on undefined values. Undefined is when I do not pass anything or I can explicitly pass the value (undefined), name will still get the value "Scott". What happens if I pass null? Well, if you've been programming in JavaScript for a while, you probably know that null is not the same as undefined. They both do evaluate to false, but null is something we typically use when we want to intentionally specify that there is some absence of a value, and it's not the same as undefined. And in ES6 we will not get the default parameter value if we pass in a null, we can only get that with undefined, either by not passing the parameter or explicitly passing undefined. Now let's put together a few things that we have learned. I have another function that takes more than one parameter this time, and it takes parameters a, b, and c, and it's just going to return them in an array. And when I invoke doWork, I'm just going to pass two parameters, I'm going to pass, let's say, a 5 for a, and since I'm using a destructuring assignment here on the left-hand side I expect there to be an a variable, I expect that to get the value 5. What happens if I pass undefined or b, well I would expect b to be 2, and since I'm not passing anything for c, I expect that to be a 3 if I save that test. We now have a second passing test. And one more example with something that we've worked with before, what happens if we have a doWork function that uses destructuring to pull out a data property and a cache property? Well, default parameters work in this scenario too. When I invoke doWork, I'm going to pass in an object that only has a cache property, thus inside of doWork I would expect cache to be false. I'm not passing a data property in that object, but since there is a default value specified here, I expect that when doWork returns data, that data will be Scott, and if I save that test that also works. So default parameters will be a nice syntax for explicitly stating what the default value of a parameter will be if it's missing. It's going to make JavaScript code easier to read and understand.
Rest Parameters
Rest parameters make it easy to work with an unknown or variable number of arguments in a function. Sometimes in JavaScript we want to write a function like sum. We want the callers to be able to invoke this function and pass arguments as individual parameters, but we don't know how many parameters the caller might pass. They might pass 3 numbers, they might pass 10 numbers. Traditionally, in JavaScript, we'd achieve this goal by using the implicit arguments object. Arguments will hold all of the parameters passed to a function in an array-like structure, but one of the many problems with arguments is how arguments looks like an array, but it's not truly an array, it's just an object that has some of the qualities of an array. Now arguments did work for these scenarios, but in ES6 I can achieve the same result using a better syntax, and that is the rest parameter syntax. A rest parameter is always the last parameter in a function, and it will be a parameter that has a ... prefix. So this version of sum has a parameter named numbers, and incoming values that are passed to this function as individual parameters will be packaged up into a true array object and given to the function in this numbers parameter. Let's take a closer look. Here in my test I have a doWork function that is very similar to what we had in the slides, but this version of doWork takes two parameters, one called name, one called numbers. Numbers is the rest parameter, it has to be the last parameter in this list of parameters, because rest parameters are greedy. They gobble up all of the parameters at that position and any later position, and they package them up into an array. So if I invoke work and pass in "Scott", that will be the name, 1, 2, and 3 will be packaged into an array, and inside of doWork I'm again computing a sum, but this time instead of using a for loop, I'm using the foreach method on an array. Later in this course I'll show you an even more eloquent way to do this when we talk about some of the new functional programming features in ES6. and after I invoke doWork and get a result, I would expect that that result would be a 6. Save the test, and the test is now passing. And you might be curious, what happens if I do not pass any parameters at that position. Will numbers come in as undefined because that could cause a runtime error, or does something else happen? Well if I save the test, you'll notice the test fails and we're told that we expected 0 to be 6, so the result that came out of doWork was 0, and that's because if you do not pass any parameters that can populate a rest parameter, it will be an empty array, which is nice because then I don't have to check for null or undefined, I can just perform operations on that array, it's going to be empty, and in this case produce the value 0. Let me undo that, put 1, 2, and 3 back, and just point out how much nicer rest parameters are compared to using the implicit arguments parameter. Not only will this be a true array and have all the array methods like foreach, but it's also much easier to look at this function doWork and see that rest parameter and know that this takes a variable number of arguments.
... Spread
Similar to the rest parameter is the spread operator. They are similar because the spread operator uses a ..., just like we use to prefix a rest parameter, but when used outside of a function argument list, the ... means we want to spread an array across individual parameters. So for the code on the screen I have a function doWork, this function takes three parameters x, y, and z, but when I went to call doWork I have my data inside of an array. Instead of indexing into the array three times to call doWork, passing array sub0 or array sub1, and so forth, I'll just use the spread operator, and the first entry of the array goes to the first parameter, the second array element to the second parameters, and so on. Let's experiment with a couple tests. In this test, I have a doWork function that takes three parameters, x, y, and z. I'm going to invoke it by passing an array with three values inside. And because I'm using the spread operator, 1 will go to x, 2 will go to y, 3 will go to z. When doWork returns, the result will be 6, and that test passes. One other interesting thing that you can do with the spread operator, is use it to build an array. What this test is demonstrating is that I can imbed 4, 5, and 6 into the array b, not as a nested array, because the spread operator will take 4, 5, and 6, and imbed it directly in the array, the result being an array of the numbers 1 through 9. That test also passes. And so the spread makes some of those scenarios where you have data already in an array, a little bit easier to work with.
Templates
In this module where we are talking about assigning values to variables, I thought I would include the topic of template literals, because template literals can help build the string values that you might want to assign to a variable, and really template literals are a wonderful feature of ES6. They will be useful for a number of scenarios. One scenario is that you can often use templates to replace string concatenation code, that is, when you need to build a string using a combination of literal text and some data. The code on the screen is building a url by combining some literal strings with the values that are inside of variables, and this string concatenation code can become a little bit tedious. With a template literal, it's a little bit different from a string. For starters, you do not delimit a template with single quotes or double quotes, you delimit it with back ticks, and inside of the back tick you can have literal text and also substitution placeholders. These placeholders are identified using a $ and then a curly block. Inside the curly block is a variable name. What the runtime will do is place the value of the variable into the template at that position. It will do that for all the substitution parameters and then return a string. Let's look at this in a test. In my test, let's start off with an easy scenario. I want doWork to build the string for me "Hello, Scott", so I invoke doWork, pass in the value Scott, but currently this test is failing and returning Hello, $(name), because what I'm using inside of doWork is not a template, it is a just a plain string literal. If I change from double quotes to a back tick and save the file, now the test passes. $(name) is treated as a substitution, the runtime will take that name value from the function argument and build the string "Hello, Scott". Another example is just what we saw on the slide, I want to be able to build a url without all the crazy string concatenation. So I need to take a category, I need to take an id, and I need to build the url 'http://apiserver/$(category)/$(id)'. If I save this file, that test passes. And then one more example that really demonstrates the power of these templates, I will uncomment this code, and first I want to draw your attention to lines 38 through 40, and what I'm trying to do is build the string 1 + 3 is 4. I do that using a template literal. I have the substitutions x and y, and also x + y, so I can have an expression inside of a substitution. But you'll notice the string that I expect is not quite the string that I am building inside of that template by itself, because IS is capitalized, and that's because you can associate a tag with a template. The tag in this case is upper, and that is going to be a function that's invoked by the runtime. So let's take a look at upper. The function invocation for a template will get two parameters passed. The first parameter to that function is what is known as the parsed template string. These are the pieces of literal text in that template chopped up into an array, such that the substitutions are removed, and then the second parameter is a rest parameter, so we know now that is also an array, and that will contain the values that are being used inside of that template, 1, 3, and 4. Now inside of this function I can interpret those incoming parameters in any way that I choose. It just so happens that what I'm trying to do inside of upper is build the same string that the template would normally produce by combining the pieces of the parsed template string with the values, concatenating everything together, but then right at the end, the string that I'm putting together I will use toUpperCase to put it into uppercase. That will make is capital IS, and if I save this test, we get the expected result, 1 + 3 is 4. But what's really exciting about this example is the possibilities that you can have with tagged template strings. You can imagine custom tags or even tags built-in to the runtime, tags that will take a template and build it, and then html-encode to help prevent cross-site scripting attacks, tags to help with localization, and since I really have no constraints with what I do inside of that tag function, and I can interpret those parameters however I want and return whatever I want, any type of object, you can imagine all sorts of possibilities opening up where someone can develop a domain-specific language that lives inside of these templates, and it's used to perform behavior like querying data and returning rich data structures. So these templates are really a big step forward for the JavaScript language.
Summary
In this module, we've seen a number of features that are new in ECMAScript 6, including rest parameters, the spread operator, and the keywords let and const. Features like the let keyword address shortcomings in JavaScript, while features like rest parameters and destructuring give us some syntax sugar to make common tasks easy and readable. These features will improve the day-to-day JavaScript code we write, by making it more intention-revealing, and in some cases safer. In the next module, we'll move beyond simple keywords to look at a feature that can change the very paradigm we use when programming JavaScript, we'll look at building classes.
Classes
Introduction
Hi, this is Scott Allen, and in this module we'll talk about the class keyword in ECMAScript 6. The class keyword brings the vocabulary of object-oriented programming to JavaScript. It's not like JavaScript didn't have objects before ES6, we could build objects, and those objects could hold data and expose methods for a caller to invoke, but the class keyword simplifies the syntax. In this module we'll see how to define a class, give a class a constructor method to provide initialization logic, we'll add methods and properties to a class, including custom get and set logic for what happens when you read and write a property, and we'll also see how to use inheritance by defining a super class for a given class.
Why Classes?
ES6 includes a class keyword for developers who are accustomed to object-oriented programming, because many languages have a class keyword, including C++, Java, C#, Python, and Ruby, so the concept is familiar to many programmers. And the goal in JavaScript is the same. The class keyword creates a class definition, and a class definition is a blueprint we can use for creating objects, that is, I can use a class definition to construct objects. That's what we call instantiating a class, when we create an object from a class. These objects will be consistent with the blueprint. If the blueprint says an instance of a class should have a doWork method, then we can be sure that when we construct an object using that class, the object will have a doWork method, and one class can create as many distinct objects as we need. If we create a class definition named Employee, we can create many different instances of the Employee class. Each object will start off with the same methods and properties, but the properties might hold different pieces of state, so each employee object might hold the name of a different employee, one is named Scott, one is Chris, one is Alex. But what's important is that while each object may hold its individual data, the method implementations for the methods on those objects, methods like doWork, they are shared across all instances of the objects. That's more efficient than placing a function inside of each individual object. How does that work? Well, behind the scenes, an ES6 class definition is creating a constructor function and manipulating a prototype object. That's something we've been doing with JavaScript for years, something you can learn about in Pluralsight's JavaScript Fundamentals course if you're not familiar with this technique. The code on the screen is just what we would write in the past if we wanted to think about an Employee class. We want to build Employee objects that have a doWork method. First we'll write a constructor function. It's typically capitalized, it doesn't need to be capitalized, there's no significance to that, but that's a convention that some developers follow when they want other developers to know that this is a Constructor function and it should be used with the new keyword. And inside of the constructor function we can initialize state for each instance of an employee, and then we'll also modify the prototype property of the constructor function to build an object that we want all employees to inherit from prototypically. When we instantiate a new Employee object using the new keyword, and that Employee constructor function, the js runtime will set the prototype of the new object to be the same as this prototype property on our function, so we will have access to do work from every object. And this constructor function and prototype setup works, but the syntax is hideous because the code doesn't reflect what we want to do in a straightforward manner. This is where ES6 steps in with a class keyword, and the code on the right-hand side of the screen will produce the exact same result. All we have to do now is declare a class, so we give it a name, in this case, Employee. Inside the class block we can define methods, like doWork. Notice this doesn't require the use of a function keyword, but we can still instantiate an object from this class using the new keyword. We still have a constructor function. We can still invoke methods on the objects, like doWork, because again, behind the scenes the runtime is doing exactly what we used to do. It's creating a constructor function, it's setting up a prototype object, but now the syntax is clean and expressive. Plus, there's more that a class definition can do that I'm not showing you as yet. Let's start digging into some code.
Defining a Class
Inside of my test, let's pretend that we're working on an application that needs an abstraction to represent employees, and I've decided to take a class-based approach. I want to be able to construct an Employee object by saying, new Employee, and then have methods on an Employee object, like doWork and getName. Currently this test is failing because Employee itself is not defined, but this is easy enough to fix. I need to use the class keyword, I want to name my class Employee, and then I use curly braces to denote the body of the class. Just adding that code and saving the file will progress me through line 9, which is let e = new Employee. I've now constructed an object. Now the test is failing because doWork does not exist. So how can I make sure that every Employee object has a doWork function? The syntax looks like this. I can just define doWork, there's parentheses there, and then there's the body of the function denoted with curly braces. There's no function keyword required, there's no colon involved, I can save this test again, and we've gotten a little bit further. Now we are invoking a function on that object called doWork, but it doesn't return anything, so the test is failing because we expected undefined to be complete. All I need to do inside of doWork is return "complete!" with the exclamation point. Now I can save the test and we have moved a little bit further. I also need a method called getName, so you can add as many methods as you like to a class definition. This one has to return "Scott", and unlike an object literal I don't need to use commas, the semi-colons are optional, and the syntax is just short and sweet. I can save this test now and it's going to pass, because I now have a function. I can invoke Employee, I invoked that with the new keyword, and that will give me back objects that have doWork and getName methods. Behind the scenes, what this is doing is just setting up a constructor function and the prototype on that constructor function. To prove that, what I could do is I could say let's expect that if I walk up to Employee.prototype.doWork, that will give me back a function that's been defined on that prototype object. I can .call that function. If you've never used call before, call allows you to invoke a function and pass an argument that you want set up for the context of that call. In other words, e will be the this reference inside of doWork. We currently don't need a this reference inside of there, but even still, I would expect this to give me back the string complete, because that third expect would be just the same as the first expect, I'm going to be calling essentially e.doWork, and if I save that test, it's still passing. Now currently we have hardcoded strings inside of methods like doWork and getName, but obviously, every employee can have a different name, so let's look at introducing state to our objects and working with a constructor function where we can place initialization logic.
constructor
Managing state consistently usually requires some initialization logic for an object, and we can implement this logic by providing a constructor. A constructor is one of the class members you can list in the body of a class. Just like the other members, it is a function, it is a function that's automatically invoked when you say new and use the class name as a function call, so new Employee will invoke this constructor function. And inside the constructor I can use my implicit this reference, which points to my object. I can use it to manipulate the object and store instant state. Let's take a look at some running code. Inside of my test, I now want to be able to construct an Employee and assign a name to that Employee during construction time. So I'll create two variables, e1 and e2. E1 should have the name "Scott", e2 should have the name "Alex". I expect that when I invoke the getName function on those objects, they will be able to return the proper name. In order to be able to do this, I'm going to need to add a constructor to my Employee. A constructor is a function. It can take arguments, so let's pass in a name when the constructor is invoked. We could use all those tricks that we saw in the last module, tricks like rest parameters and destructuring if we wanted to, but for this example we're just going to take one parameter, a simple string parameter, and now I need to save that somewhere inside of the object. The implicit this reference will be pointing to the object that's being constructed. If I do an assignment, something like this._name =, that incoming name value, I should now have a key on my object, a new attribute that I can read from _name, and it will contain that name. And if inside of getName, instead of just returning a hardcoded value, if I return this._name, and save this test, it's now a passing test. I have two objects with different pieces of state inside, but because of the way prototype inheritance works in JavaScript, they are sharing the implementations of doWork and getName, and getName is always going to return the proper data because it's using the this reference properly. The this reference is still required to look up data stored in the object itself. Many other languages also use implicit this variables or me variables to represent the object a method is associated with. JavaScript is a little bit different since the this reference can be manipulated, but also because this is required. If I wasn't using this when I say this._name, JavaScript would go off looking for a variable named _name, not in the object, but in the current scope, and it wouldn't find one. I could demonstrate that by changing the implementation of getName to just return _name, and you can see _name is not defined, we're not looking in the object now, I need to have the this reference in place. And what you're seeing is not terribly different from what you might have done in years past with JavaScript if you use constructors and prototypes. Instance data is placed directly into the object with the this reference inside a constructor function, and indeed, the function named constructor, that is the function that is invoked when constructing an instance of the Employee class. Then we have the prototype object with all the shared methods attached. What's happening behind the scenes is exactly what we've been doing with constructors and prototypes all along, again, the syntax is just a lot nicer.
get and set
Once you have some data inside an object, you might want to protect the data. We'll talk about hiding information later. In this section I want to talk about encapsulation with getters and setters. Getters and setters are created using get and set keywords. They effectively bind a property of an object to a function. A getter function will get a value, so you can return a value to the caller, and a setter function will take a parameter you can inspect and use to set a piece of data in an object. So we're reading and writing properties through functions, and the big picture is that getters and setters allow you to now have code execute when someone is reading or writing a property on an object. And this little bit of indirection allows you to work all sorts of magic because you can dynamically compute the value of a property or you can perform validation logic when someone sets a property, all sorts of possibilities. Let's take a look at how getters and setters behave in a test. Inside of a new test, I still have my Employee class. It's exactly the same class as we were using in the clip. I'm still creating two instances of that class and assigning those two objects different names, Scott and Alex, and I have two passing asserts that prove that each object is storing the proper name. I'm checking that by invoking a getName function and making sure it returns "Scott" and "Alex" for e1 and e2 respectively, but I would prefer, as a client, a nicer syntax of just being able to say e1.name and still getting the result "Scott", and I would prefer to say e2.name to get the value "Alex". That's what I would prefer as a client. As the author of a class, though, I would still like to be able to have some code execute when someone tries to get the name out of this object. I might want to do that because I need to modify the name that's being returned or I need to log every time someone checks a name, but whichever is the case, I can satisfy both of those constraints by using a getter instead of a function. Now a getter looks just like a function. There are still some parentheses here, there's still a function body, there's still a return statement. This function takes no arguments because it's just reading data and returning it to the caller, and it has the get keyword in front, and this will work. If I save the test, it's still going to pass. But now I have the ability to write any code that I want inside of here, compute values, modify values, change a name to uppercase, and now the test will fail because it's expecting, really, an uppercase "SCOTT", and an uppercase "ALEX", and I can do whatever I need inside of this getter. I can also write a setter, which looks much the same. It's going to be essentially a function, but this function's going to take a parameter, it's going to be the new value that someone is trying to set, and I can say this._name = this new incoming value. Of course, I could also perform any sort of validation that I want, so I could check this and make sure the new value is not undefined, it's not null, it's not an empty string, that it meets some minimum length criteria, that it doesn't exceed some maximum length. If any of those validations fail, I could throw an error and not set this new name value. I'm going to leave the logic inside of here very simple, but you can certainly imagine a number of validation checks or logging statements that will execute every time someone tries to set a name. And setting a name, just like reading a name, I can say e1.name =, let's give e1 the name "Chris", and then I would expect if I call e1.name that will be "CHRIS", uppercase, save that test, it's still passing. So as a client, I get to this name property of the object without using parentheses, I just do .property to read the property or .property = to set the property, but behind the scenes, getters and setters are really wrappers around that property that allow me to execute whatever code I want, and I typically need some sort of backing field, in this case It's _name. I need somewhere to store that information that someone is giving me, so when they try to get it we can retrieve it and hand it out. And if I didn't want people to set it, I wouldn't have to provide the setter. I could temporarily comment this out, save the file, and notice we get the interesting error, Cannot set property name of this Employee object, so I effectively have a read-only property. And people still get to _name, yes, they could circumvent all of this logic and just walk up to let's say e1._name and get that value or set that value. Everything I put into an object is publicly available, and object is essentially just a dictionary and we'll talk about how to hide things a little bit later in the course. For now, I just wanted to show you get and set and how well they work with the class. The interesting thing about get and set is they're not new in ECMAScript 6. Getters and setters were actually defined in ECMAScript 5.1, but you really don't see them widely used throughout a lot of code bases, and that's primarily because they were only supported in Internet Explorer 9 and above. These days, many people have moved past supporting Internet Explorer 8 and 7 and 6, and so you should feel free to be able to use get and set, particularly if you're using any other ECMAScript 6 features. But they work so well with the class definition, that's why I included them in this module. When you're building a class, you're typically trying to build some sort of abstraction, you're trying to model something, maybe hide some information or hide some implementation details, and getters and setters are all about encapsulating some state that you might have inside of an object, being able to know when someone is reading that value and, perhaps, performing some computations before you return it, and knowing when someone is trying to set that value so that you can perform some validations or clean things up and trim strings and parse integers, and so forth, before you actually set that value. Note that if I did have some validation inside of the setter I would probably want to say this.name =, the incoming name in my constructor, and if I save this test, we'll be back to passing. But note that this is no longer going to be placing a property or an entry inside of the object that has the name of name. This.name is now going to be calling the setter to assign a new value to underscore name. And if you do want to store a value, you typically need some sort of backing field, that is some sort of attribute on this object that will store that value and keep it around. If I didn't have that and I tried to do something like this.name.toUpperCase, return that, thinking I'm accessing some property _____ of my object, well if I save this test, I will get an error that the Maximum call stack size has been exceeded, that's a stack overflow error, and that's because inside of the getter when I tried to return this.name and read the name property, that's going to call the getter, which will call the getter, which will call the getter, and we will keep allocating stack space until there is an error. But if I go back to using my backing field, which is _name, save this test, everything is working again.
Inheritance
When I was learning about object-oriented programming years ago, I learned about the three pillars of classical object-oriented programming. These pillars are encapsulation, polymorphism, and inheritance. ECMAScript 6 gives us an easy syntax to specify an inheritance relationship, which is a way to say that a class inherits from another class, and thanks to inheritance that class will have all of the methods and state defined by that other class. As an example, let's say I need to write a Person class, and every Person has a name property. I can read and write that name property and perhaps I have some logic to do some computations and validations inside of the getter and setter. Now I also need to model an Employee, and since every Employee is a person and has a name, maybe I'll just have the Employee inherit from Person. In ECMAScript 6, inheritance is specified using an extends keyword, and now anytime I instantiate an employee, an Employee object will have a name property that it inherits from Person. I can then start to add additional features to Employees, features that are specific to employees, things like a job title or what happens when they do work, but at the same time I am reusing the code from Person. Since every employee has a name, I don't want to duplicate the code to create and manage that name, I'm just going to inherit the name instead and reuse that code. Inheritance is all about reuse, and the general rule of thumb is that we can use inheritance whenever there is an "is-a" relationship. So an employee is a person, just like a zebra is an animal or a credit account is a general ledger account. These are all scenarios where you might have some base class, like Person is a base class, and it contains the generic code that you can reuse through other specific classes that derive from that. So since an Employee is a Person, I'm going to reuse Person by extending it and inheriting that name property and all the code associated with it. Now before we jump into a test, just a couple of notes. First, inheritance was possible in previous versions of JavaScript. You could manipulate prototype references so that you could have an Employee inherit from a Person. There are various libraries that you could use to make that easy to set up, but all of them were a little bit different, so once again, we see that ECMAScript 6 is standardizing the syntax for a common operation, it's giving us an easy technique for achieving inheritance and code reuse. With that being said, inheritance is only one way to reuse code. It's arguably one of the worst techniques for reusing code, at least in my opinion. Inheritance does create a tight coupling between pieces of code, and sometimes it can be hard to understand and unravel that code when you're new to a code base or you're returning to some classes that you wrote six months ago. However, inheritance can be useful in some scenarios, so let's take a closer look. Inside of a test, I have a Person class defined. When I instantiate an instance of this Person class, I can give each object a name to store for that Person, and then I have a getter and a setter to read and write the name. Currently, this test is failing because I do not have an Employee class defined, so the line of code that constructs a person will work, but the line of code that constructs an Employee will not currently work. What do I expect from an Employee? I expect two things, I expect every Employee to have a name. In this case I expect the name to be Christopher. And I expect every employee to have a method doWork that I can invoke and get back the string name is working, "Christopher is working". So I could do this by writing an Employee class and duplicating the code that I have to get and set a name, or I can use inheritance and say class Employee extends Person. And just by having that definition there I can save this test, and it's still failing, but the error is going to change, because now I can construct a new Employee, and that Employee is going to have a name that works properly. What I don't have currently is a doWork method, and this is a case where I am inheriting from Person or extending Person because I want to reuse that name code, but then an Employee is probably going to have some special state and special capabilities beyond and above what a regular person would have, and I can just add those in here like I've done before. So if I want a doWork method available for an Employee, I will add a definition for doWork. And in this case, what I want to return is this._name is working. So we will use that literal syntax that we learned about in the last module, and now if I save the test, everything is passing, and I have an Employee class set up, such that when I instantiate an Employee it inherits all of that code from Person and all of the qualities of a Person, but also extends that Person by adding a doWork function. We would say that Person is a base class for Employee. In some object-oriented circles we think about base classes. In other object-oriented circles and in JavaScript specifically we talk about a super class. So Person is the super class for Employee. That's just another fancy way of saying Employee inherits from Person, and we could continue constructing an inheritance hierarchy this way, so we might have Managers, and Managers are special types of Employees. They need all the abilities of an Employee, like a doWork method, but they also might need some additional methods and additional state, and then we could say that an Executive extends a Manager. And when we build an inheritance hierarchy like that, where Manager is the superclass of Executive, and Employee is the super class of Manager, and Person is the ultimate super class, then an Executive would have all of the qualities of a Manager, and an Employee, and a Person, so it would have a name property, it would have a doWork method, and it would have whatever else is defined inside of Manager and the Executive class itself. Those types of inheritance hierarchies, deep inheritance hierarchies can be dangerous, because they can be difficult to understand inside of an application. But like I said before, they can be useful for some scenarios. I'm not going to build that type of inheritance hierarchy here in the test. I think you can take what we've learned here and just picture additional classes extending the other classes. Instead, I want to think about a different scenario. Let's say that an Employee, in addition to having a name, also needs a title, because most company employees have some sort of title. So now we need an employee to store two pieces of state, a name, and a title. We need to initialize an employee with a name and a title. This is where things can get a little bit interesting with inheritance. We'll look at that scenario next.
super
There are times when you use inheritance where you will want to interact with your super class in a very specific way. In fact, there's a couple scenarios when you must interact with a super class, and one of these scenarios is when making sure the super class initializes properly. For example, if I have an Employee class that extends Person and I expect an Employee to have a name and a title, then I'll probably have a constructor that requires a caller to pass in the name and the title when it's constructing an instance of Employee. Now setting up a field to store the title inside the Employee is no problem, only Employee objects will have a title, Person objects will not. But what do I do with the Person's name? How do I get the name into this object? I don't want to duplicate any code that might be in the Person constructor. What I really want to do is forward that name along to the Person constructor so that the constructor defined in that Person class gets to do its job. In JavaScript, this has to be done explicitly, and it is done using the super keyword. With the super keyword, I can forward a call from the Employee constructor to the Person constructor, and pass along parameters required by the Person constructor. There's also many other interesting things that I can do with the super keyword. We'll get to those. Let's take a look at this scenario in a test first. In this test, I have the exact same classes that we were using in the previous section, Person and Employee, that extends Person, but my tests expect to use the Employee class in a slightly different way. Now when I create an Employee, I want to pass in the Employee's title and their name, and I would expect that e1.name would be "Scott" and e1.title would be "Developer". Currently this test is failing, and it's failing in an interesting way. Notice that both the asserts are failing, and the first assert is failing because it expected "Developer" to be "Scott". That's the expected as on line 146 looking at e1.name. So what is happening here? Well, when I construct a new Employee, because Employee does not have an explicit constructor, what the runtime is doing is calling the Person constructor and passing in "Developer" as the name. Therefore, when I look at e1.name I'm getting out "Developer", but what I really want to do is assign "Developer" to title and have Scott be the name. That means my Employee is going to need a constructor and now once I have this constructor, I'm not going to place any other changes here yet, I'm just going to save the file. Notice my tests are still failing, but now even the name is undefined. So as soon as you add a constructor to a derived class, then even if the super class has a constructor in place, it's not going to be called. It was called previously because I did not have this constructor. I'll cut that out again, save the test, you can see that the e1 object has a _____ property and e1.name is "Developer", because that's the first parameter that I pass in when I'm constructing an instance of Employee. That goes directly to the Person constructor. But now that I add my own constructor to Employee, a Person constructor does not get called. So this constructor is going to take a title and a name, and I can say this._title = that incoming title. I could write a getter for title, so get title is something that will return this._title, save this test, now I have one failing test. I have one failing test because now we are calling the Employee constructor. It does get the title and the name, it will assign that title value to _title, but now what's happening to the name? Name is not being assigned, my Person constructor is still not being called. I could, if I wanted to, inside of Employee say, this._name = name, and I could save this test, and everything will pass, because even though I'm setting _name in a derived class, Employee, that attribute is still in the object, so when someone says e1.name, which calls this getter, and it looks inside this object, it is going to find _name, but this is not the proper approach. Imagine if the constructor for Person had some validation logic inside of here, maybe it performed some calculations on that name, maybe it's going to cache the length of the name, because that's used in other calculations in this object. I would be skipping all of that initialization logic if I did this. What I really want to do is use that super keyword and pass along the name. I can use super like a function, in which case, what super will do is invoke the same method in the super class, so when I'm inside the Employee constructor and I say super, and I can pass whatever parameters I want or no parameters, that will invoke the same function in my immediate super class, so that will invoke Person constructor and now if I save the test, everything is still working, and I'm not duplicating the code that initializes the name. Now the super keyword is not restricted to use only inside of constructor. I can also use it inside of other methods. I can use it inside of doWork I can invoke super. Well, that would be an opportunity for me to call the same method in my super class, a method called doWork, which currently we don't have, and this opens up all sorts of interesting possibilities for reusing code and overriding method definitions. For instance, if I do have a doWork inside of Person, perhaps it provides some default implementation of what happens if a Person works, but that behavior can be different when I have an Employee object, because maybe when we ask an Employee to doWork we have to keep track of how many hours they work because we need to pay them, where as a Person who is not an Employee, perhaps they're going to do some work for free. Let's dig into this scenario a little bit more in the next section.
Overrides
Inside of test, I have the exact same code that we were just working with, but I've copied it into a new test so we can do some experiments. The first experiment I want to do is I want to show you what happens if I actually invoke e1.doWork, because we've added this call to super, and if I save this test, we're going to have a failure, because at this point the JavaScript runtime is going to go looking for doWork in the super class, which is Person, and we do not have a doWork method there. So let's change some things around and experiment with this a bit. I'm going to temporarily comment out this doWork, and I'm also going to change around the tests a little bit. So let's construct a new Employee and also a new Person. The Person's name will be "Alex", and let's write some different tests. What I want to happen is I expect when I call (p1.doWork) I want to get out the result "free", because this is just a Person. But if I do (e1.doWork), I want to get out the result "paid" because that person is an Employee. Now if I save this test at this point, we do not have a doWork method anywhere, so let me add one to the Person class. We will have a default implementation of doWork, which will just return "free". And now if I save the test, we're going to have one assertion that passes, that's the assertion that calls p1.doWork that is going to return "free", but so does the Employee. e1.doWork works, that is, we actually invoke the function doWork, the function on that Employee object that is inherited from its super class Person. So now what I want to do is add a doWork to Employee also, and if I change the implementation of doWork to return "paid", and save this file, we will now have a passing test. What we've done is override the implementation of doWork inside of Employee. We've provided a different implementation of that method, what many people in object-oriented programming would call overriding that method to give it a different implementation that's going to behave differently. But I can also inside of doWork, return super or some combination of super and my own custom logic. If I save the test right now, what we'll get back is "free paid", because the call to super returns the string "free" and then I concatenate "paid" on the end. Of course, that doesn't make any sense, but I do just want to show you that you can invoke a super class method using that super keyword, in fact, you can be very explicit about this. If I just use super and parentheses, that's going to invoke a method with the same name in my super class if I say super.doWork, that will also invoke doWork. I can save the test that fails with the same error, we will return the same result, but I can explicitly call anything inside of my super class by saying super dot, and then a method name. So if there was a method called foo, I should be able to invoke that. Of course, that's not going to work. What I don't want to do is return this .doWork, because when the doWork method is resolved through the this reference, this is a reference to Employee, and we'd be invoking the same doWork method that we're already in, so if I save this task we'll get the maximum call stack size exceeded, a stack overflow. Because we're just calling our self recursively, I want to go back to using super. Another method that I could override that might not be obvious at first is a toString method. Implicitly, when I declare a class and I don't say that it extends anything, it actually does extend object by default, and object comes with methods like toString that I can override. Let me demonstrate that. We'll use the implicit inheritance, and what I'll do is I'll fix this test up to work. Right now we just want to return "paid". But let's say that if I do an expect on p1.toString, I want that to come out to be "Alex", and if I do an expect on e1.toString, I want that to come out to be "Scott". Currently if I save this file, we're getting the default implementation of toString that is provided by the object type in JavaScript. It returns object, object inside of square brackets. I want to change that behavior to return names. Since both Employee and Person just need to return the name, I could add this to our Person class, and say when anyone invokes toString, I want you to return this.name, save the test, everything is working again. I am now overriding the toString method that I inherit from object. And it works from Employee and it works from Person. So where is this type of behavior useful? Well, it is sometimes useful to be able to work with a set of objects where all of those objects have a specific method that you can invoke, but each object might behave a little bit differently, because one object is an employee and one object is a Person. They might do different things, but I might be able to write an algorithm that doesn't care about the details of what doWork will do, it just needs to invoke that method and maybe harvest the results. So, for instance, if I have a function called makeEveryonework, and let's have it take a rest parameter called people. What I want it to do is loop through people, tell each person to work, and collect the results. So I'm going to have an array of results that represent what happens each time I call doWork, and then I will have a for loop that loops through people, and for each person it will tell them to do work and what I need to do is say results.push into that array, the result of what happens when you call people{i}.doWork. And now I can write a test that says, I would expect that if I call makeEveryoneWork and pass in p1 and e1, I would expect the result that comes out toEqual the following array. The first thing I pushed in was a Person, so I would expect the first result of that array to be "free", and the second result of that array to be "paid", and now I can save this test, and that test will pass. And, again, the beautiful thing about this is the implementation of makeEveryoneWork doesn't really care if an object is a person or an Employee, all it needs to know is that there is a doWork function that can invoke, and that function is going to collect all of the results together and hand them back to the caller. What would happen if I passed in an object that wasn't a person or Employee? Well, obviously now we are going to fail because that object does not have a doWork method. If I wanted to have some check for that, there are several ways I could do this. I could say, if people{i}.doWork, if that is truthy, so that's an easy way of saying is there a doWork function available. That would be about the simplest possible test, and it really expresses what I want inside of this algorithm. I just want to make sure that the object I'm working with has a doWork method. But I could also do some other checks. I could check if people{i} instanceof Person. So if you haven't used instanceof in JavaScript, it's been around for quite some time, it is a check that will return true or false, does this object have the person.prototype in its prototype chain, and for every object instantiated by using new Employee and for every object instantiated by using new Person, this check instanceof Person will always return true. So if I save this test, we're back to passing, because that check will skip the object that wasn't created with new Employee or new Person. If I were to check for just instanceof Employee, that test would only pass for e1 when that was passed in. So I get back an array that is just "paid", and we've skipped calling doWork on the Person. If I go to instanceof Object, well then all three of the things that I'm passing in, they are all objects and the problem is that last object doesn't have a doWork method. But generally speaking, these types of checks are only useful for defensive programming, that is, I just want to make sure that everything passed to me or everything that I'm operating on is an instance of Person. What I wouldn't want to do is check if instanceof Person, do this thing, else, if instanceof Employee do this other thing. I can put those different implementations into my class details. From here on the outside, inside of a function called makeEveryoneWork, I shouldn't be switching behaviors based on the type of the object, if it's a Person or an Employee, that should really be baked into the classes. But it is useful in JavaScript sometimes to use instanceof, just to make sure that you're getting the right type of parameter.
Summary
In this module, we looked at the class keyword introduced by ECMAScript 6. The class keyword gives us a sweet syntax for building object blueprints using constructor functions and prototypes. We saw how a class can define methods for an object, as well as getters, setters, and a single constructor function. We also took a look at how to use inheritance with the extends keyword. If you are familiar with object-oriented programming from other languages, the class keyword will feel very comfortable right from the start. You also might be wondering if ECMAScript 6 has the ability to define the accessibility of class members. Are they public, are they private? The answer is no, there are no keywords to address accessibility or visibility, but later in the course when we talk about modules, I'll show you how to do something I hinted at when we talked about the class keyword, and that is how to hide some implementation details.
Functional Programming
Introduction
Hi, this is Scott Allen, and this module is about functional programming with JavaScript. JavaScript has always been a functional programming language, but ECMAScript 6 improves the functional ability of the language by adding new features, features like arrow functions. Arrow functions not only give us a compact syntax for defining functions, but they can also make some scenarios safer. We will also look at iterators and generators, which give us the lazy evaluation features so many other functional programming languages offer. So we'll see a number of examples where ECMAScript 6 has introduced a new syntax for a common programming operation. We'll start this module by looking at arrow functions.
Arrows
Arrow functions introduce a concise syntax for defining a function of executable code, and this compact syntax is a wonderful feature to have in a programming language like JavaScript, because we create functions and pass functions as parameters quite frequently. The code on the screen demonstrates what an arrow function looks like. We are creating a function and assigning it to the variable add. We can then invoke add to get some sort of result. The arrow function gets its name from the two character symbol used in the function expression the = and >, it looks like an arrow pointing to the right. Other languages have this same feature, and they call this operator the rocket, the fat arrow, or the Lambda operator, but the ES6 specification uses the term arrow function. On the left-hand side of the arrow will be the function parameters for the function. In this case we have two parameters and we give them the name x and y. They have to be surrounded by parentheses. Then on the right-hand side of the arrow is the function body. This is the code that we want to execute when the function is invoked. We just want to compute the sum of x and y. We do not need curly braces or a function keyword or even a return statement. JavaScript will know that we just want to add these two numbers and return the result. So the function is short and to the point. There's no code ceremony required with curly braces and keywords. It's a very short and expressive statement. Let's take a look in a test. Inside of a test, I'm defining a function to add two numbers. I'm using the syntax that has always been available to us in JavaScript for defining a function, but now I'd like to change this into an arrow function. All I need to do is remove that function keyword, I'll leave my two parameters here inside of parentheses. I can remove curly braces, I can remove the return statement. The only thing I need to add is the arrow so that JavaScript still defines x + y as a function and I can still invoke that function through add. If I add 2 and 3, I expect to get the value 5, and that test still passes. These parentheses are required, not only because I'm using more than one parameter. In other words, if I have to build a function that only requires a single parameter, like a function to square a number, then I don't need the parentheses. I could just say x goes to x * x, and now I should be able to square the result of adding 2 and 3, and if I save that test, of course it's going to be failing because the result should now be 25. I also need parentheses if I have a function that takes no parameters. So if I want to write a function that just returns the value 3, I have to use empty parentheses, and then say return the value 3. I can use that instead of a 3 here, just invoke that variable, and that test is still passing. Now you can, if you need to, write multiple lines of code inside of an arrow function. When you do that, though, you will need to add the curly braces to create a block of code, and you will need a return statement. So instead of just saying x + y, let's create a temporary variable to hold x + y, and return temp, just to demonstrate that that code works, the test will still pass. Where Arrow functions really provide an advantage, though, is when you can just write a short, simple expression that really reveals the intention of what you need to do without all the keywords and functions and returns and curly braces. For example, there's a number of very useful methods on an array where you need to pass in functions. So let me uncomment this test, actually I'll leave these two lines commented out, and save it, and it's going to be failing because I'm taking an array with the values 1, 2, 3, and 4. I want to compute the sum of all the numbers that are in that array. I would expect that result to be 10. One way to do that would be to write a for loop, pull out each element of the array and add it to sum, but I could also do that with the forEach method of an array. So forEach is one of many methods on that array which effectively employ a strategy design pattern, because the methods are very good at doing one thing, like iterating through an array, or in the case of a filter method it will iterate through an array and pull out selected items, but these methods don't know what strategy that I want to employ to filter these items or what I need to do as I foreach through each element in the array. So all of these methods I can pass in a function, and what they will do is give me each item from the array, like the number as the variable n, and then it's up to me to decide what I want to do with an n. Instead of using the function keyword, though, I can use the arrow function syntax, and say give me the number, put it into a parameter I'll call n, and I just want to add n into sum. And if I save this test, it's now passing, and I have a little bit of executable code there that is my strategy for what I want to do for each number. Here's another example. I want to take that numbers array and I want to double it, so it's not 1, 2, 3, 4, I want to create a new array which is 2, 4, 6, 8. I could do that with the map method. So, again, map is very good at iterating through the array, but it doesn't know what strategy I want to execute for each item that it pulls out of the array, I have to pass in a strategy that describes how I want to transform or map each item. Since I want to double them, I could say let's take a parameters n, and just return it as n * 2, save the test, I now have 2, 4, 6, and 8. So while Arrow functions have this nice expressive syntax, they also have one additional feature which can make them very useful. Let's talk about that next.
Arrows and Asynch
You'll probably come across situations where you want to use arrow functions as a callback for some asynchronous activity. Joe is going to talk more about asynch programming later in this course, but I want to point out a special feature of arrow functions at this point. Most JavaScript code has a lot of asynchronous behavior inside, because every time we need to communicate over the network or wait for an event, we need to use, ultimately, callback functions and the arrow gives us a terse syntax for creating those functions. But one problem with callbacks is managing the this pointer inside of code, because the this reference inside of any particular piece of JavaScript code will be established by the context of how a particular function is invoked. So if I invoke a function through an object, like e1.doWork, I'll have a this reference that points to the object e1, but if I invoke a function as a callback from inside of doWork, the this value inside of that function can be different, it can point to global scope instead of the object. As an example, in this test, it's very simple, I'm saying this.name = "Scott", and then I should be able to expect that this.name will be "Scott", and indeed that test passes. Now without going into too many details about the Jasmine test runner, this does not point to global scope, it's going to point to a scope that has been established by Jasmine for this test function, but all of this can fall apart if I do something asynchronous inside of here, for instance, make an ajax call or simply execute some code inside of a set timeout. So what I want to do is execute some code after 15 milliseconds. The code that I want to execute is actually my expect assertion, and I do have to tell Jasmine to wait for this code to execute. I can do that by taking a done argument to my test function, and then I need to invoke done to tell Jasmine when this test is actually finished. So now I have the same code as before, the same expect, but now that expect is inside of an anonymous function that is invoked by set timeout, and the this reference is going to be different. It's not going to point to the object where I created name = "Scott". Now there's been various solutions to this that we've used over the years, for instance, it's quite popular to create a variable called self, and assign it the this value, and then throughout the rest of the code simply refer to self instead of this, and if I save that test it's back to passing because my anonymous function that I passed to set timeout has effectively formed a closure around self, and it saves that value, it's not relying on the this pointer, which can be a little bit slippery. But instead of taking that approach, I'm going to hit undo and go back to using this, but I'm also going to change this function to an arrow function. It doesn't take any parameters, I just need to remove the function keyword and put the arrow in place. And now if I save this test it's still passing, because arrow functions will always capture the this value of the context that they are inside. We say it lexically binds to this. So because this arrow function is defined inside of a function where this is established by Jasmine, my this reference inside of the arrow function will be the same as the this reference inside of my test function. Those two will always be the same. And if I have additional arrow functions defined inside of that arrow function, the lexical binding continues and my this reference just never changes, so I no longer have to worry about creating a self closure or somehow binding functions to make sure the this reference is set up appropriately. With arrow functions I can just write my code, I don't have to worry about my this reference changing.
Iterators
ECMAScript 6 standardizes iterators and iterables. These have been around in other languages, even JavaScript itself, but they've never been standardized before. Here's the idea. Let's say we have an object that holds a collection of items, and when I say collections I use that term loosely. A collection might be an array of items or it might be a set or a map, those are two data structures that Joe will talk about later in the course, but the collection could also be objects that are computed on the fly or a custom data structure like a tree structure. So when I say collection I'm using it in the abstract sense to mean any sequence of objects. If an object that holds a collection is iterable, I can walk up to that object and retrieve what is known as an iterator, and an iterator allows me to walk through the collection one item at a time, because an iterator is an object with a next method. Every time I call next, I get the next item in the collection. That item is placed into an object with a value property, the value holds the item from the collection, and there's also a done property. It will be true or false, telling me I've reached the end and should stop calling next. So I will keep iterating until I reach the end or until I've found some item that I need. An iterator is a wonderful abstraction that is hiding the source of the items. I have to use the iterator to get each item, and I don't care if the items are in an array or a set or a map or the DOM of the browser. Because iterators are an abstraction, they do take away some features that I might expect form a more complex data structure like an array. For example, I do not know how many items an iterator might return because there is no length property for an iterator. I can only use the iterator to go one item at a time, and eventually I'll reach the end. Then I'll know how many items I have. So while an iterator doesn't have nearly as many features as an array, we actually gain some flexibility because of this abstraction. What I'd like to show you eventually is how iterators are lazy and they do the least amount of work possible. That means our code might run faster in some scenarios. I also want to show you some of the beautiful features and higher level of abstractions that we can achieve with iterators, but before we do that, let's try to understand the iterators at a low level in a test. Inside of a test, I have an array of numbers and I want to compute the sum of all those numbers. I could do that with a for loop. Because an array has a length property and it's easier for me to set up a loop with a variable that goes from 0 to less than length. I'll use that variable to index into the array, pull out each value, add it to my sum, and the first passing expect here would be that sum is equal to 10. I can also do this with a for in loop. For in can iterate an object and pull out the available keys. In the case of an array, those keys are the available indexes into that array, so I can use each value of i to index into numbers, pull out the value, and again I'll compute a sum of 10. Now let's try this approach with an iterator. If I save this test, currently failing, we have to figure out how to compute the sum using an iterator. The first step would be to get an iterator out of that array. One way to do that is to use the values method on an array. Values does not return an array, it returns an iterator, an iterator that allows me to walk through the values 1, 2, 3, and 4. And really, the only thing I can do with an iterator is to call next, and I'll need to keep calling next until next returns an object whose done property is equal to true, and that means I need some sort of looping construct here. I'll use a while loop, because I can say while(!next.done), then I'll be able to keep calling iterator.next, and in the meantime I will add next.value into my sum. And now let me save this test. We have a passing test. So that's what the iterator API looks like at a low level where you need to explicitly get the iterator and call next, and typically set up some sort of loop where you inspect or manipulate each value and then call next at the end to move to the next item, but there's also a new construct in JavaScript that makes this very easy and readable to use. Let's look at that next.
for of
JavaScript has always had a for in construct we could use to loop through arrays. We saw an example in the previous test just before we worked with an iterator, and you probably know that for in works by enumerating the properties of an object. This means we can iterate over an array because for in will work with the available indexes inside an array, but we can also use for in against an object and fetch property values, because in this code for in will retrieve the first and last key entries. Then we just use those entries to index into person, and we will essentially console.log all the values inside that object. And while this behavior is useful, it's not always what we want. Sometimes we just want to iterate over the values and not worry about the keys or indexes in an object. In fact, with some data structures we can't really even think about keys, like with iterables, and the for in construct cannot work with iterators. So ES6 introduces a new for of looping syntax. This looks just like for in, but the word of means we will be looping over values, not keys, not indexes, we will loop over the values so we can use them directly. So every time I console.log(i), I will have one of the numbers of that array, 1, 2, 3, or 4. One of the benefits of this type of loop is that it can work with iterators, because behind the scenes before of loop can call the next method and check a flag for us to see if iteration is complete. Let's take a look in a test. In a previous test, we computed the sum of the numbers inside of an array by using .values to retrieve an iterator, and then working with an iterator at a very low level, but I can achieve the same results using for of. If I write for(let n of numbers), n will receive each value from that array, 1, 2, 3, and 4, so not the indexes, which would be 0, 1, 2, 3. And all I need to do is say sum += n, and if I save the file, the test, which is currently failing because we weren't computing the sum, is now passing. And so all that you have to remember to work with iterators is that you can use for of. Now if you want to go off on a little bit of a tangent and dig into what's actually happening behind the scenes with for or, then one thing you might notice is that I'm not saying for of numbers.values,l up here I had to use values to get the iterator. And the question is, how does for of get to an iterator? Well, there will be a special method defined on any object that is iterable, any object that has the ability to give you an iterator. It's a somewhat magic method. I could get to the same method here. If I use the syntax Symbol.iterator, and save this file, the test is still passing, but this will require a quick explanation of Symbol. I want to return to Symbol later in the course when we talk about modules in JavaScript, but for now this is everything you need to know. If I can create a Symbol, which is an object, but it's a function object so I can invoke it, I can create a Symbol and every Symbol has a somewhat random value associated with it. The reason the random value is there is because you can use a symbol as a property name in an object, and the somewhat random value means that the property you create won't conflict with any other properties that might be in that object. It also means that programmers won't know the value of a Symbol, they won't know how to get to that property, unless they have access to the symbol itself, and this is just a fancy way of saying, I could create an object, let's call it x, it's a new empty object, I can create a property on that object or a method on that object by using my Symbol, and now the only way for someone to get to that value is to either know the Symbol is $4468, but the next time we run it, it might be 1, 2, 3 or 7, 8, 9. The only way to really get to it is to use that Symbol, now they can retrieve a _____, and then Symbol.iterator, you can think of it as a constant that the JavaScript runtime sets up for you to get to that special method on any object that is an iterable. So if x was, instead, an array of numbers, I could ask for x{Symbol.iterator}. You can see that gives me back a function. If I invoke that function, that gives me back the iterator that I need, and that's exactly what form of will do is it will look for Symbol.iterator, retrieve that property value, and if it's a defined property it will invoke it to get the iterator, and then all we need to do is start calling next and we will get those objects out with the value and the done flag. So that's a quick introduction to Symbol. We'll talk about it more later in the course. For now, just know that you can use for of to work against objects that are iterable, and behind the scenes it is the Symbol.iterator function that every iterable object must implement. Let's implement one next.
Build Your Own Iterable
Inside of this test, I want to show you how you can make your own classes and objects iterable. There's a hard way to do this and an easy way to do this. I'm going to show you the hard way first. Not only will that give you a better understanding of what's happening behind the scenes when we look at the easy approach, but it will also give you some ideas of what you can do if you need the utmost flexibility in building your own iterator. What I have so far is a class, Company. A company class will manage some collection of employees. You can add employees to a Company object at any time. So in the test I'm constructing an instance of company and adding four employees. I'm using a for of loop. I want that to be able to loop through the employees that are in a company and count them. I would expect that if I just added four employees this count would be four. But currently the test is failing because for of cannot find that magic iterator function that would make a Company object iterable. What I need to do is add that magic function that has a name defined by Symbol.iterator. I have to use the square bracket syntax inside of my class in order to be able to use that and have a method on my object with a proper name, so just Symbol.iterator in square brackets. And now if I save the file I know that was an effective first step because the error has changed. The error has changed to cannot read property 'next'. So now I know that the for of loop has invoked that function, and it thinks it's going to be working with an iterator, so it's going to call next. That's the first thing that you would do when you get an iterator, but we're not returning any object from here yet. I would need to return an object that has a next method, and if I save the test again I know I've gotten one step further, because now the for of loop has invoked that function, it's gotten back an object, it invokes next on that object, and it's checking the done flag to see if iteration is finished. So at this point, I could continue building out this next method, but I want to take a different approach at this. Since we know how to define classes now, what I want to define is a new class called ArrayIterator, because I can place the code for an iterator inside of this class and then reuse it from any object that I want to make iterable. I would expect that a class that is going to provide an iterator for me for an array, would need a reference to the array that's going to be iterated, so I'm going to pass in this.employees when I create an instance of the ArrayIterator. And now I just have to implement ArrayIterator. It will have a constructor that takes the array to iterate, it will save that array off as part of the state of an object, and it's going to need a next method. The first time next is called, I need to pull the first item from the array, put that in an object and give it back to the caller, but I only want to do that if there is something in the array. I'll probably have a check inside of here like if this. what, well, let's create something, this.index, and start it off at 0. And now every time next is called I can increment this.index, and when that index reaches the end of the array then I know iteration is finished and I need to return an object with a done flag of true. Here I can place the check if this.index is < this.array.length, then I want to return some sort of object. I want to return an object that has a value property and a done flag. Let me just create a default instance of the object that I have to return. It will have a value property. I will initialize that to undefined, it will have a done flag, I will initialize that to true. This is exactly the information that I want to return to the caller if the index has reached the end of the array. So I'll have return result down here at the bottom, but if index is less than the array length, then I can say result.value = this.array{this.index}, and also result.done = false, because we have not reached the end of the array. And now I have something where I should be able to save this file and the test passes, because now the for of loop will walk up to the company object, invoke this function to get back an array iterator, and it will keep calling next until we reach the condition where done is true. And now I have this class, ArrayIterator, that I can reuse in other places where I want to build some sort of iterable object. However, there is an easier way to build an iterator, I'll show you that next, but before I do that I just want to point out something that's really nice about iterators. Although the logic in here is going to step through the array one item at a time, I could have logic inside here to do anything. I could have logic that starts at the end of the array and works back to the front, or I could have logic that automatically sorts the employees by their name or salary or title or position if we had more complex employee objects. I could even change how I store Employees. I might not keep them in an array, I might keep them in a more advanced data structure, but it doesn't matter what I change as long as I can still provide an iterator like this, none of the client code has to change. That's one of the nice things about iterators. Another nice thing is that now I can allow people to loop through my employees without giving them a reference to the underlying array that I'm using. I don't want people to be able to change that array to push things into it or pop things off of it, and another advantage of having this iterator is that I can give clients all the information that they need, but without any of the possible dangers of giving them the array directly where they could change it or without any of the memory problems that I might have if I made a copy of that array to give back to the client.
Generators
A generator function is a function that generates an iterator. A generator function doesn't require a lot of code, but it does require a new keyword, the yield keyword, and a special function syntax. Notice the star on this function declaration. Inside a generator function defined with a star, instead of using the return keyword to return a value, I can use the yield keyword to return multiple values, and the meaning of the word yield is significant. The first reason yield is significant is because the function does produce a value each time it uses yield, just like a fruit tree will produce fruit or yield fruit. And I can yield multiple values, because the generator function is like a factory for iterators. The iterator object that the generator function creates will allow me to step through each item the generator function yields, just by calling next, just like we did with our low-level iterator code against array values. And the second reason the yield keyword is significant is because each time a generator uses yield, it yields the thread of execution back to the caller, that is to say the value is returned to the caller with yield immediately, the caller can do whatever it needs with that value, perform calculations, do some logging, push the value into an array, and then when the caller asks the iterator for the next value, execution will return to the generator function just where it left off. So the first time a client of the generator function calls next will execute inside that generator function until we reach a yield. Then control returns to the caller, they will process that value, and when they call next we jump back into the function and execute code until we hit the second yield statement, and this will continue until there are no more yield statements left, at which point the object that the generated iterator returns will have a done flag of true. We sometimes talk about generator functions suspending execution when they yield, and then resuming later when someone tries to get the next value. That's a nice way to think of a generator. Let's take a look at this crazy magic in a test. Inside of a test I have a generator function that is going to yield the numbers 1, 2, and 3. Then I have some code to invoke that function and produce an iterator. I'm going to call next on that iterator, go through all the values, and try to compute a sum. I would expect that the sum of 1 through 3 will be 6, and that's certainly true. Now let's play around with numbers a little bit. Instead of hard-coding numbers and writing three yield statements, I can also use a yield inside of control flow statements. So, for instance, I could write a for loop and say for i = 0, i <= 3; i++, let's yield i, and if I save this test it's going to produce the same result. I could also take parameters to this generator. So I could say, give me a starting number and an ending number and I will go from the start to the end and yield i each time. Let's go from 1-4. And, of course, if I save this test it's going to fail because now the sum will be a 10. Let's add some logging statements inside of here to see how execution is working. If I do a console.log of the number I am about to yield, and then I also do a console.log each time before I invoke next, then when I save this test we're going to see that I call next and we'll enter into the generator function and execute until we yield a 1. We jump back out, call next again, jump back into the generator function, execute code until it returns a 2. We call next for a 3, next for a 4, and then the last time we call next there are no more yield statements that execute, the function completes, and that's going to return to us an object with a done flag of true, so we're finished with iteration. Now previously in this course, we worked with an iterator at this low level, and then replaced the code with a for of, and I can do that here also. I can say let n of numbers from 1-4, now I no longer need the iterator. I'm going to place this console.log with a next at the top of the loop, take my code to compute a sum, and console.log("next") inside of a for loop, this will be sum += n, and now let's get rid of the rest of this code, let's also change a number and say that we go from 1 to 5, save this test. You can see that execution behaves the same. Of course, now our sum will be a 15, but hopefully you can start to see how to think about this code. You can think of for of as generating the code to work with an iterator. It's calling next, it's checking a done flag, it's implementing all that code that we just had there with the iterator. And you can think of the generator function as creating some sort of iterator object like we wrote in the previous section. We wrote an array iterator. It had to have a next method. That array iterator had to keep track of the state of where we were in the iteration. It did that with an index property. And it takes some time, I know it took me a while, but if you think about that scenario it starts to demystify some of the magic a little bit. How can we just jump into a generator function and pick up where we last left off. Well, we can do that because behind the scenes this generator function is really creating an iterator that is a lot like a state machine. It's keeping track of where we are in the iteration. It has a next method that every time I invoke it I jump back into that iterator. And this generator function is just a higher level syntax that provides me with that object, and every time I call next I'm jumping back into the code that that generator function is producing. And this means I can go back to my previous example where we wrote our own iterator class and I can replace that code with a very simple generator function. Let's do that next.
Putting It Together
Previously in this module we built a Company class and we made objects instantiated from that Company class iterable by building that magic iterator function inside that used this ArrayIterator class that we wrote, but I am now going to delete that class and use a simple generator function instead. A generator function always uses that * inside of a class body. The correct approach is to place the star in front of the method name. So if addEmployees was a generator function I would add a star there, but it's not, and so I'll leave it off. And now I can write a simple for of statement and say, let e of employee just yield e. And I can save this file, and the test still passes, but instead of writing 12 or 15 lines of code I've only written 3. I will add a console.log statement here so that I can demonstrate some behavior later on. Now you might have reached this point of the module and said to yourself, gee, I thought the topic was functional programming, but once we got past arrow functions we've only been looking at iterators and generators. I don't understand how that's very functional. Well, I included these topics in this module because iterators and generators allow you to build functions that compose together very well, that is functions where you pass in other functions or you take the result of one function and it matches right up with what another function expects as an argument, and I'd like to demonstrate how that works. Also, the generator function specifically, exhibits some behavior that we really like to take advantage of in functional programming, and that is lazy evaluation. So let's imagine this scenario. Instead of iterating through all the employees of the company, I want to be able to find just specific employees. It might be employees who work a certain number of hours or employees whose salary exceeds a certain threshold, but since we're just dealing with employees who are strings, let's say we just went to find the employees whose name start with a capital T. So I effectively need to filter this collection as I'm iterating through it. Well, there are lots of libraries out there that provide filtering capabilities for you, but it it's fun and educational to build your own. So let's build a generator function called filter, and what filter will need is the items that you want to filter something out of and a predicate that it can use to test if an item is something that you want or not. And this filter function will be generic. It can work against anything that is iterable, because inside I will just say for item of items, if I invoke your predicate, so predicate will be a function that I pass into this function, if I invoke your predicate and pass in the item that I'm currently on, you return true or false. Do you want this? And if you do want this, I will yield that item, otherwise, I'll skip it and go to the next one and test it. So it's certainly possible to run the Company through this filter function and not get any employees back because the predicate always returns false, but I plan on writing a predicate that tests for the capital T, and that should return at least a couple employees. Let me also add a console.log to know when we are filtering or examining a specific item. And now down here right before the assert, I can filter the Company and I need to pass a predicate. I can do that with an arrow function. I can say, given an employee e, since it's a string I can just look at e sub 0, and if that is equal to a capital T, that is one of the employees that I want to count. If I save this test, it now fails because we've only counted two employees, Tim and Tom, so I just need to change my expect, and now the test passes. Notice how we yield Tim from the company generator function, then we filter Tim in our filter function, and then we go to Sue, and then we go to Joy, then we go to Tom. It's as if we're pulling these items through some sort of pipeline. We're pulling the items out of Company and passing them through a pipeline that goes through a filter, and it can go through anything else that we need. So let's say that we only want to take or examine or count some specific number of employees. Maybe we have 100,000 employees, but we only want to count to make sure we have at least one employee that has a name that starts with a capital T. And it would be nice if I could do that without iterating through every employee. Once I find an employee with a capital T I'd like to be able to stop iteration. Well let's build another function, a generator function called take, because I only want to take a certain number of these items. So you give me the items to iterate, you give me the number of items that you want, the maximum number of items. I'll make sure I never yield more than that number. I'll do that by setting up a counter. I'll start it at 0. We should probably have a special case here to say if number < 1, I will just immediately return, yes you can have a return statement inside of a generator function. When you hit a return statement, the iterator that this generator creates will set the done flag to true. So if you pass in a number of 0 or -1, the iterator will immediately say, we're finished with iteration, the done flag is true. Now I can write my for statement. So for item of items I'll do a quick logging statement so we know when we are taking an item, and then I can immediately yield that item. I'll need to increment my counter, and then I can check if count has reached the number, we'll have a return. So once we've reached that maximum number, we'll have a return statement that will set the done flag to true. And now what I should be able to do is say that I want to take out of this filtered company just one single result and save this test, and it of course, fails, because the count will no longer be 2, now we're only allowing a single item to come through. But let's take two items, let's say we want to make sure we have two employees that match that criteria, we're back to passing. But in order to point out an important behavior of what's happening here, let me set this back to 1 and expect a 1. The test is still going to pass, but you'll notice that we did not have to iterate through the entire collection. There might have been 100,000 employees inside of this collection, but as soon as we found the first matching employee, Tim, we filtered on that item and it passed, we took that item, and then because our take function returned and essentially said we're complete and finished with iteration, we never had to look at any additional items. And this is the beauty of iterators and generators and how they can be as lazy as possible and do the least amount of work possible. In fact, if I did not have this for statement, of course, my test is going to fail, but let me show you that if I create an iterator by walking up to Company and asking it for Symbol.iterator and I save this test, notice that we didn't filter or take or yield any items because my code never asks for any of the items. I never did an iterator.next. I need to pull an item through the pipeline with a call to next. That will force that code inside the generator function to execute up until it reaches a yield. If I save this test, I did yield the first item, which is Tim. So going back to the for statement, you can think about this as only executing iterator.next, a finite number of times, in fact, only twice, once to find the value Tim, and then the next time it executed the take function short-circuited everything and said, we don't want any more items, so iteration finished. And this is how generators and functional programming with lazy evaluation can sometimes prove to be very efficient, because it's lazy.
Calling Next
There's one more capability of iterators that I want to make you aware of. Consider this range function. It's a generator function that will yield the values between start and end inclusively. Below that generator function I have some code that's going to use range, and the low-level iterator code to build an array with the results that come back. Currently, this test is failing because I'm asking for a range from 1-10, but I'm expecting the result to be 1, 3, 5, 7, 9, which is just odd numbers. And now the question is, how can I make this test pass? Well, of course, I could use a filter, but what I want to show you is that since next is a function call, I can pass a parameter into next. And the value I pass in can influence the state of the generator because inside the generator I can receive that value and inspect it and perform some different computations. Where I would receive it would be after I have yielded some value back to the caller. If the caller calls next and passes me something, that will be the result of the expression yield current. So I could say let delta = yield current, and now if someone passes a 2 I would receive a 2 as the delta. If they don't pass anything, if they call next without passing anything, I'd get an undefined. But now if I said current += delta or 1, in the case it's undefined, and I save this test, it's now passing. Now you might be wondering if there is a practical application of this behavior. Well, later in the course, Joe's going to give you a practical example of where this works really well. For right now, I just want to demystify how this behavior actually works. Let's consider another function, I'll call it range2, and this is not going to be a generator function, this is just going to be a normal function, but I am going to return an object that can be used as an iterator, because it will have a next method. And down here in my test, I'm going to want to invoke range 2, get back an iterator, and achieve the same results I had before. And so I'm showing you, sort of, what this generator with a yield statement would create if we were to look at a possible implementation of what the iterator might look like that the generator is creating. So, inside of here I will keep track of the current number that I want to yield, I'll start with that start number, and of course, next we'll have to return a result, which is an object that has a value property. I will initialize that as undefined. It will have a done flag. I will initialize that as true, and that will be my default result. But if my current value is <= the end of the range, what I can do is say result.value = my current value and result.done is false because we are not finished yet, and then I need to figure out how to increment current. Well because next is a method, I can take that delta parameter here, I can even use a default value syntax to give it a default value of 1 if nothing is passed, but where do I use that to increment current. This part can be a little bit tricky. This is not the code that I want to write. If I save this test you can see it's failing with 1, 2, 4, 6, 8, 10, it's not quite the same as a generator code, and the reason is that I am incrementing current too late. I need to take this delta value and increment current by delta before I yield a value, but not the first time next is called. Why is that? Well, because technically the first time I call next on an iterator, I'm not allowed to pass a value here. That would be trying to change or influence the initial state of the iterator. If I want to change the initial state of the iterator, it would be easier just to change the parameters that I used to create the iterator, so I would change the arguments to range2. And another way to think about this so it might make sense is that on that first call to next, I have no ability to capture that inside of generator code, because inside of generator code when I say something like let delta = yield current, I don't receive that delta value until after yield current has executed, given the value back to the caller, they've done some work with that value, and then now they're calling next. The first time they call next, we're executing the code that starts off this function and hits the first yield. So if I were to try to pass a value into that first call to iterator.next, there's simply no where to capture that. And to simulate that inside of range2, what I could do is set up a flag that essentially says, is this the first call to next. Down here I will say, first = false, to make sure we always set that to false after the first call, and then I'll have just one additional if statement. If this is not the first call to next, only then will I take current and add in this delta value. So the first time we call next, we're simply going to return the start value, since I assign that up here, but on each successive call to next, I'm either going to increment by a Delta value that was provided or 1, which is the default value, and now if I save this test, everything is passing again. And the important part to remember here is that working with low-level iterator code I can pass a value into next, and inside of a generator I can receive those values that are passed into next and I can use them to influence my decisions on the future values that I'm going to yield. Joe is going to give you a great example of where this can be useful later in the course when we look at asynchronous programming with JavaScript.
Comprehensions
The last ES6 feature for this module is the comprehension syntax of ES6. Comprehensions have a very functional feel to them. You might already know something about comprehensions if you've done functional programming in Python or Haskell. Comprehensions are a terse syntax for building arrays and generators. First there is the array comprehension. It is an expression built inside of square brackets, that's the first clue to know that you're building an array, inside of those brackets I have what looks like a for of statement, in fact, that's just what you should think of when you see this syntax. I have a for of statement where the variable n will represent each item from some collection. In this case, the collection is an array literal 1, 2, 3, but it could be any iterable object. I do not use the let keyword when defining the variable n, a let is implied and n will be scoped to the inside of this expression. And then after the for of I have a piece that represents the value that I want computed for each n. In this case, I want to square n, so I'm building the array 1, 4, and 9. This is just like writing a for loop and creating an array using the code on the right-hand side of the screen. I have a for of loop, I have pushing n * n into an array, but the array comprehension syntax is compact and more declarative compared to that imperative code on the right. I can also use a predicate in the comprehension to filter values. So in this example I will only be using values of n when n is greater than 1 and then there is the generator comprehension. The generator comprehension uses parentheses instead of square brackets, so we are not building an array, we are building a generator that will give me values I can iterate lazily. That's the key difference. The array comprehension is greedy. It will immediately build the entire array for me as a concrete data structure. The generator comprehension syntax with the parentheses is more like the generator functions that we've looked at. They use yield, and they do the least amount of work possible. Let's look at some other examples. I want to return to the scenario where we have a Company class that can iterate through a collection of employees. Notice I'm going to log as I yield each employee this time. And this is the scenario where we built a filter function and a take function. Those two generator functions will allow me to add four employees to the company. I'm going to go and look for the first employee that starts with a capital S and take just one result. I will also log what I receive inside of this for of statement, and I expect to find just one employee, and I expect that found employee's name to be Sue. Currently this test passes. You can see that the company iterator yields Tim, it yields Sue, we got Sue, everything is lazy, everything is finished at that point, and the test passes. Now let's investigate this filter function. Once you become comfortable with the comprehension syntax, you'll start to see many for of statements that you write as candidates for rewriting with the comprehension syntax. And so now, instead of doing a for of and writing imperative code to yield each item, let me just try to yield, and we'll use the array comprehension syntax. So for (item of items), I want to produce item. And if I save this test, the first thing to notice is that we yielded Tim through Tom, so we yielded all four employees, and notice that what I got was not an individual employee, it was the array of all employees. That's what I produced, and that's what I yielded. So one of the first problems here is that I haven't applied the predicate, and that's easy to do if I say if(predicate(item)), only then include the item in this array. Now if I save the test, a couple things to notice. I still yielded all the employees, but now I'm applying that predicate correctly so the only item that I'm saving is the "Sue" item, but the test is still failing because what I am yielding here is an array. What I really want to yield will be individual items of that array. And it turns out there's an easy syntax for that with JavaScript in the yield statement. If I just append a star to the yield, that's like applying a flattening operator. That's like saying, I want to take an array and yield each item individually. So don't return the entire array to the caller, return each item individually. And now if I save the test, the test passes, I got just the individual item, "Sue", but what I want you to notice is that the company iterator had to iterate through all the employees, Tim, Sue, Joy, and Tom. That's not what we were doing before, we were a little more lazy. The reason it is iterating through all of the employees is because the array comprehension syntax is a greedy operation. Whatever you specify as the source collection here, the array comprehension is going to iterate that from beginning to end. But if I change this to a generator comprehension, with the parentheses, so now I'm not creating an array, I'm creating something a little more abstract, I'm creating something that can be iterated, and if I save this test, you can see that the company iterator does the least amount of work possible, it just has to yield Tim and Sue, and thanks to the take operator, which short-circuits everything once I reach the required number of items, those are the only two employees that I had to iterate through. And that's the primary difference between the array comprehension and the generator comprehension. Use the generator comprehension when you want to be a little bit lazy and perhaps save yourself some work, but use the array comprehension when you want to have everything just inside of a concrete data structure right from the start.
Summary
In this module, we started off by looking at the arrow function in JavaScript. This is a wonderful addition to a functional programming language like JavaScript, because the syntax is compact and expressive and it lends itself well to passing around functions as parameters, which we do so often in JavaScript. We also learned about the new for of syntax and how to create generator functions and use iterators to move through the values that a generator can yield. Combining these features together with comprehensions leads to some interesting opportunities to write lazy functional code without the clutter of syntax. I believe these changes will dramatically change the JavaScript code we write every day, and we'll also see how these changes allow for more robust data structures. Joe will talk more about that later in the course.
Built-In Objects
Introduction
Hello, I'm Joe Eames. In this module we'll be looking at some changes that ECMAScript 6 will be bringing to built-in objects, which will include existing objects and some new ones. we'll look at how Numbers are different in ES6 and some new functionality on the Math object. we'll also see how the array object is changing, and we'll look at some new objects, Sets and Maps, which have both a regular version and a weak reference version. In order to look at the examples from this module, I'll be using two different browsers, Chrome and Firefox Nightly. Now by the time you watch this, all the functionality may be available in the released version of Chrome or Firefox, or by some miracle, Internet Explorer. So even though you'll easily be able to identify which browser I'm using, check the ES6 compatibility chart, try out the functionality in your favorite browser, and then if it doesn't work, use the same browser that I'm using.
Number
In this section, we're going to talk about the changes that ES6 is bringing to Numbers. Let's first talk about how ES5 handles Numbers. In JavaScript, all numbers are internally housed inside of a single type called Number, but there are several different literal types of numbers that we can use inside of our code. The most obvious, of course, is decimal, but ES5 also supports hexadecimal. To create a hexadecimal literal, you use a leading 0 and x. At this point, I can type in a, and this variable will be the hexadecimal value a, which is the decimal value of 10, but ES5 also supported octal numbers. Unfortunately, they didn't use a clear literal value with octal numbers like they did with hexadecimal numbers. So instead of a 0x, in order to create an octal number you just use a leading 0. This is now an octal number. Let's consider the following test. Here I've created the octal value 071, so this could easily be mistaken for the value of 71. In reality, I've created the value 57. If we run this test, we'll see that it's going to pass. The octal literal 071 is the decimal value of 57, and since it's internally represented by a number, even passing it the parseInt doesn't fix it. You can see that our test is still passing. To alleviate this problem, ES6 is introducing two new number literals. There's now a new number literal for octal. This uses a leading 0 and then the o character. Now you can use a lowercase or an uppercase o, but as you can see, it's a bit easier to read the lowercase o. Then you specify the hex number. This is the same value that we see on line 4, and we'll prove that with a test. And now if we run this in our browser, you can see that the hex literal is now supported. The other literal type that ES6 is introducing is a binary literal. You create a binary literal by prefixing the number with 0b, and then you put in the binary number. This binary number is the value 13. Let's run this test as well. Now if you get these literals as a string value, say from a text file, then you can parse them into numbers using the number function. We'll put in the octal value 071 again, and we'll assert that that's 57, and we can parse binary literals the same way. And that's the binary representation of 5, and you can see that our tests are passing. Now the Number object also has some new functions in it in ES6. One of the things that ES6 has done is exposed the parseInt and parseFloat functions on the Number object. This is just an attempt to clean up JavaScript just a little bit, so that instead of using global functions, you're using methods that belong to an object that makes sense. Here we're testing the parse int. We'll parse the string of 3 into the value 3, and we'll do the same thing with 3.5. Number is also now exposing the isFinite function, which was available in ES5, but in this case the functionality has actually changed. The difference is, the global isFinite function actually runs the value through the Number function to convert it to a number if possible. This is done before the logic is executed to determine whether or not the number is finite. So, for example, when we call the global isFinite, it's going to take this string "1", convert it to the value "1", and then run the isFinite logic, but when we use the static version exposed by Number, it no longer runs the conversion, and a string value is not a finite number. Let's run these tests in the browser to prove that they work. Number is also doing the same thing with the isNaN function as it's doing with isFinite. It's re-exposing it, but also not running any conversions. So we get the same functionality with isNaN as we get with isFinite. Here the string "NaN" is converted to the value "NaN", so it's going to be true, but here when calling the static method on Number it's not going to get converted, so it's not going to be the value NaN. The Number object is also exposing a new function called isInteger. This will correctly detect whether or not numbers are integers or floats. So isInteger with a value of 1 would be true, but it will also work correctly with 1.0, which is represented as the integer value of 1. And, of course, if the number is not an integer, then it's going to be false. Numbers in ES6 are also bringing us one new important capability, and that is the ability to detect safe integers. JavaScript has always been able to represent extremely large numbers, but it hasn't been able to safely deal with numbers over 2 to the 53rd. So, for example, given the following test, you would expect this test to fail because, obviously, 2 to the 53rd is not the same value as (2,53)+1. Well, unfortunately with JavaScript, those are actually represented by the same internal value. So you can see that that test is actually passing when we should have expected a failure, and it actually gets worse the bigger the number gets. Those two numbers are 2 apart, and yet the test continues to pass. So, we have two new values on the Number object which are max safe integer and min safe integer. These specify the largest values you can safely deal with inside of JavaScript. These values are 1 less than 2 to the 53rd positive and one greater than 2 to the 53rd negative. Corresponding to that, Number also exposes a function called isSafeInteger. This is the decimal representation of the largest safe integer. This is going to test that this function returns true, and this would be one greater than the largest safe integer, so this function is going to return false. Now let's run this final test. So there you can see some of the new capabilities of Numbers and the Number object inside of ECMAScript 6.
Math
ECMAScript 6 has also introduced several new functions on the Math object, so I've created a new spec to look at Math objects. Let's go into our runner and let's change it so it's only running the mathSpec tests. There are a lot of new functions on the Math object, but we're not going to be spending too much time on them. First off, there are several new trig functions. The Math object already had most of the standard trigonometry functions, such as sin and cosine, but in ECMAScript 6 it introduces several new trigonometry functions. For example, Math now introduces the hyperbolic acosine function, and there's also the same for sin and for tangent. They've also introduces the hyperbolic cosine function, which is cosh, sin, and finally tangent. These are functions that you might not use very much and they're fairly easy to replicate in ECMAScript 5, but if you do use them a lot, they will be convenient having been included in ECMAScript 6. Let's go and run those in the browser. The Math object has also introduced several new miscellaneous functions, some of which are going to be more useful than others depending on what you're doing. The first one is the cube root function. Again, this is something that you can do already in ES5 just using the correct formula, but this makes it just a little bit easier. There's also a new function called clz32. This returns the number of leading 0's in a 32-bit representation of the number. So, for example, 5 in binary would be 101, which takes 3 digits. Out of 32 bits, that leaves 29 digits for leading 0;s. There's also several new functions when dealing with logarithms. The log1p is the natural logarithm of the number + 1. There's also a new log10, which takes the logarithm _____ 10 and log2. There's also expm1, which returns the base of the natural logarithms e raised to the power of the number given, -1. Again, for certain applications this can be very useful, but something that you may not find yourself using too often. An interesting new function is the hypot function, which is short for hypotenuse, which takes a variable number of arguments and returns the square root of the sum of the squares of the arguments. So in the classic method of finding the hypotenuse for a triangle, 32 + 42 is equal to 52. And finally, we have fround. This will take a given decimal number, such as 2.888, and return the closest value that's actually able to be represented inside of JavaScript using JavaScript's number system. In this case, the closest value to 2.888 is this value given here. Again, when doing very particular mathematical operations, you may find some of these to be useful. There's a third set of mathematical functions that have been included inside of ES6 that you may be able to use slightly more often than the ones we've already shown, but again, this depends on your application and what you're doing. First off, there's a new sin function which will tell you the sign of the argument given. For a positive number it will return 1, for 0 it will return 0, and for a negative value it will return -1. Another function that you may find useful is the truncate function. This will truncate off the decimal portion of a number. It's different than floor because on positive numbers it will round down, where on negative numbers it will actually be rounding up. Now let's run these new tests in the browser. So there's a brief overview of the new functions on the Math object in ES6.
Array
In this section we're going to be talking about arrays and the new features that ES6 adds to them. Arrays have always been a core data structure inside of JavaScript, and ES6 only adds to their capabilities. There are several new functions that have been added to array instances that make it a lot easier to do certain things with arrays. The first function we're going to talk about is the find function. Array instances have been given a new function called find. Let's see an example of that. Here you can see I'm calling the find function on an instance of an array. Now it doesn't matter if I create an array variable or an array literal, the find function still works the same. So this is the same as this. At this point, you pass a callback into find and it will return the first element for whom this callback returns true. So here I've passed in a callback that returns whether or not the item is greater than 8, so of course, the first matching item is going to be the value 10. You can see that find is as straightforward as it is convenient. The next function we're going to look at is findIndex. FindIndex works just like find, except it returns the index of the first matching element. And, of course, the first item that's greater than 3 is the value 5, and 5 has an index of 1. The next function we'll look at is the fill function. The fill function will actually fill in all or portions of an array with a single value that you specify. You simply call fill, and pass in the value that you want it to fill. And if that's the only value you pass in, then it will fill in the entire array with that value. Now, of course, I could put in any index here, not just 3, because every element in this array is now the value a. In addition to calling fill like this, there are two optional parameters that we can pass into the fill function. After the value that we want to put into the array, we can also pass in a starting index. In this case, the index 2 means that the value 3 is going to be where we're starting. We can also pass in an ending index. This is the index after you start, that you don't want to be filled in. So, for example, if I only want to fill in one value with the value array, then I'll pass in 3. That means that only the value at index 2 is going to be filled in. In this case, the value at index 2 will be a, but the value at index 3 is still going to be the number 4. The next function we're going to look at is the copy within function. This function copies portions of the existing array on top of itself in a different location. So, for example, if we had the following array, and what we wanted was an array that looked like this, we could actually use copyWithin to do this. In order to understand copyWithin, you have to think of this as having two pointers. The first pointer is the target, and that's where you're going to be copying the new elements to. That's also the first parameter, so in our case we want to start copying at index 2, which is where the 3 is. The second pointer is where you're going to be copying from, which is also represented by the second parameter. In our case, we want to start copying from the beginning of the array, so we'll pass in the value 0. At this point, there are two pointers, one that points at index 2, one that points at index 0, and the pointers will begin copying elements one by one and moving to the next element. Now it's important to note that it's going to copy the values of the array according to how it looked before the copyWithin method started. So even if you start writing on top of yourself, it will still use the old values and not the new values. So in our case, calling it like this will produce our desired value. There's also a third optional value, which is where you're going to end. So if we put in the value of 1 here, then we're only going to be copying one element, which will be the number 1 on top of where the number 3 goes, so we would end up with an array that looks like this. So in our case, passing in a 2 is the same as passing in no value because it's going to hit the end of the array and stop copying elements. Additionally, the start and end values can be negative, in which case they indicate an offset from the end of the array. So instead of producing this array, we want it to actually produce an array that looked like this, then our target would be the 0 element, that's where we're going to start copying into, and we're going to start copying from -2, and we don't need a third parameter because we're going to hit the end of the array and quit copying. This is one of those functions that in order to really get it you should play around with it a little bit and see how different parameters work, but it's also one of those functions that's only useful in limited circumstances. So in this case, we can test that our first element and our second element are now 3 and 4 respectively. And that's the copyWithin function. Now we're going to look at a function that deals with one of the quirks of arrays in JavaScript. This is the of function. The of function creates a new array. In the current version of JavaScript, when you want to create an array the common way to do it is with the literal syntax, like this. But there's another way to do it, and it's using the array constructor. To create an array that had a 1 and a 2 in it, we would just pass in 1 and 2, and that would produce an array that looked like this. If we also wanted a 3 in there, we could just add another argument, and if we wanted them backwards we can simply reverse the order and then they'll be produced like this. But, if all that we want is an array with a 3 in it, we can't call this because that will actually produce an array with 3 elements, all of which are undefined, and the array will look like that. Now most often it's better to just use the literal syntax to create an array like this, but the of function can come in handy under certain conditions such as sub-classing arrays. So in this case, the first array is going to have a length of 3 where the second array will have a length of 1. The next function will come in handy a lot more often. That's the from function. The from function will create a new array when given an array-like object. For example, when you use document.querySelectorAll, you will get an array-like object that isn't actually an array. This function will grab all the divs in the html document, but this is not an array. So, for example, it doesn't have a forEach function. What does make it array-like is that all of the items can be accessed by an integer index and it has a length value, but if we want to create a true array out of this we can use the from function and a true array object will have a forEach function. Like sets and maps, arrays are also getting a couple of iteration functions. First they're getting the entries function. Let's grab that first entry and the entry comes back as an array with two elements. The first element is the index and the second is the value. There is also a keys iterator. Keys, of course, are just the index, not the value. So when you call keys, all you're really getting is a sequential list of integers.
Array Comprehensions
The last feature of arrays that we're going to cover is comprehensions. Array comprehensions are a language feature where you create a list based on an original list by doing some kind of an operation to each item in the original list to produce the items in the new list. JavaScript has had this feature for a while using the map function, but array comprehensions can make this syntax much easier to read. We'll start out by looking at the syntax for array comprehensions using a very simple example. To create an array using an array comprehension, first we use the open bracket, which is just like the array literal syntax, but then we start off with the for keyword. This tells the compiler that we're going to be doing an array comprehension. The next piece is inside of parentheses we create a variable. In this case I'll call it i, use the of keyword, and pass in the list that's going to be the source for our new list. In this case I'm just going to create a new array by using the array literal syntax, but I also could have passed in a variable that was an array. Then outside of the parentheses we tell the compiler what we want to do to each item in the original list in order to produce the new list. In this case I don't want to do anything to the original item, I just want to use it unaltered, so I'll just list the variable i, which tells the compiler that I want to use the original element without any kind of manipulation. At this point, the array that we've produced is going to look exactly like the original array. Now let's vary this just a little bit. Let's start off with the same original list, but this time let's take each element and square it. This will produce an array with the squares of the original list. Array comprehensions also let you filter the original array. We do this using an if keyword, then inside of parentheses we put in our if clause. I'm going to say that i has to be less than 3. Then I'm just going to put out the original element. Since the third element of the original array won't pass this filter, it's going to produce a new array with only two items in it, 1 and 2. Now even though our examples have been extremely simple, you can see that there's a lot of power in the syntax. There's one other thing you can do with array comprehensions, and that is create Cartesian products. Let's say that we're creating an algorithm that will help people name their babies by showing them all the possible combinations of first and middle name that they've picked out. We can sell this easily and succinctly using an array comprehension. We'll start off by creating a new array, and using a comprehension, we'll put in the first names. In our sample example, we'll have three possible first names, William, John, and Blake. Then we'll add in another for clause, which will be the middle names. In this example we'll use Robert, Andrew, and John, and now we just output the two together. And, of course, we want to output the last name as well. We'll just use Smith as an example last name. Now the very astute may be aware of something. One of the first names is John, but it's also one of the middle names. Now, of course, there's no reason to name a child with both the same first and last name, so let's add in an if clause that will filter out any matches for which the first and last name match. And now our comprehension is done. Let's go ahead and log this to the console, just so we can see how it looks. And we'll write a quick test that our first element is exactly what we expect. Now let's go run this in the browser. Looking at our console, we can see that our array has the values William Robert Smith, William Andrew Smith, William John Smith, John Robert Smith, and so forth, but you'll notice that there is no John John Smith because we filtered out any matches for which the first and middle name match. So there's an example of using multiple for clauses in an array comprehension. You are also not limited to two dimensions in your Cartesian product, so if your algorithm requires more than two dimensions, that's easily accomplished with array comprehensions. And that will conclude our look at the features of arrays in ES6.
Set
In this section, we're going to take a look at our first new object, the Set object. Sets are a new data structure added in ECMAScript 6 that really adds some nice features to what you can do with JavaScript. Up until this point, we've always used arrays or objects as our data structures, since that's all we had. To keep a unique list, you might use an object, but if all you needed was the key and you didn't need the value, you still needed to put something in for an object. In addition to that, objects had their own properties, so it might require some additional code to make sure that the existing properties don't show up as items inside of the collection. Now we have the Set object, which allows us to represent a unique set of items. For example, let's assume we are building an application that allowed users to vote for something by sending in a text message and we wanted to limit those votes to only one vote per phone number. A unique list of all the numbers that we've collected votes for would allow us to determine whether or not a new vote should be allowed or disallowed. This would require quite a bit of work with an object, but with a Set it's extremely simple. All we have to do is check the collection for the existence of an item, and if it's not there then we add the item in to the collection. Sets have quite a bit of functionality. For example, you can determine the size of a Set by using the Size property. Here I've created a new Set and checked that it's empty by making sure that its size property is 0. Adding items to Sets is as easy as calling the add function. And, of course, the size property has been kept up to date. When using objects to hold collections of data, any key that you give an object will be turned into a string. For example, when you use the string 1 and the number 1, ultimately they will become the same key. This is made even worse when you try to use an object as a key. JavaScript will toString the object, which usually ends up with something like the word object as being the string representation of that object, but with Sets you can use objects as keys. Let's create a key object, and we'll use that in our set. So, for example, if you have a list of objects that represent people, you can put those objects into your set. So, in essence, whatever you put into the set as a key will be the exact same thing whether that's a primitive value or an object. Another nice feature of sets is that you can initialize them with an array. All you have to do is pass the array into the constructor. When this happens, each item in the array will be added to the set as a distinct item. This is one of those features that currently only works in _____ pure release versions of Firefox. Let's run that in the browser. So you can see that here in Firefox Nightly the tests are passing. What's really nice is that Sets don't allow you to input duplicate values. How Sets handle that is by simply ignoring additional values. If you want to take a specific action when a duplicate value is entered, you'll have to check first using the has function. You can see that even though I've called add twice on the Set, because I've added the same item in twice, the size is only going to be one. Sets also offer a clear function, which will remove all items after being called. Here, even though I've added three items, calling clear removes them all and the size is 0. And Sets also expose the delete function, which allows you to remove a specific item. I've removed the 1 with delete, but the 2 still exists. So if I tested the size of this set, it would 1. We can also iterate Sets using several methods. The first is a forEach function exposed by the set, which takes in a callback as an argument and will run that callback against every item in the set. Because sets are not an ordered list, the forEach function will be called on each item in insertion order, but this is typically not something that you should rely on. If you need an ordered data structure, you should typically use an array or a more complex data structure. I've created a set with three strings in it. Let's call forEach and count the number of times that it's invoked, and I'll use the new arrow syntax in ES6. There's also another way to iterate through sets using ES6's new for of syntax. I'll do a similar test as above and count the iterations. This syntax can often be easier to read than calling the forEach function. Sets also have three functions that expose the collection of the set as an iterator. The first of these is the entries function. The entries iterator will return an array every time next is called. This array will have two values. Both values will be the key value of the set, so they will be identical to each other. Remember, with iterators, when next is called two properties return, value and done, so we'll just grab value. And this value that's returned, again, is an array with two elements, both of which are the key for that entry in the set. The next function is keys, which is an iterator which just returns the item in the set. You can see the difference by comparing this to the previous test. The final feature I'm going to show you about Sets is the fact that not only can you construct it with an array, but you can actually construct it with an iterator. Since the values function is an iterator, we can use that to demonstrate this functionality. Now I'll create a new Set by constructing it with the values iterator from the first set. This will duplicate set1 into set2. They won't be the same set, they'll be different sets, but they'll have the same values. Now I just used values to show you an example of using an iterator in the construction of a set, but if we truly just wanted to duplicate a set, because set supports for of functionality, we can actually duplicate it just by passing in a set into the constructor of another set by taking off this values function. I'm going to put the call to values back because this test is about constructing with an iterator, but you can use it either way. Now let's go and run our tests and check that they're all passing. And there you've seen the functionality of the Set object.
Map
In this section, we're going to look at the second of our two data structures, Maps. Maps are a collection of key value pairs very similar to objects that are actually designed for the purpose of holding collections of key value pairs. Maps are very similar to Sets, except that each key has a corresponding value. Like sets, the keys in maps have to be unique, and also you can use anything for the key of a map. Up to this point, when creating key value pairs using an object, every key that we created was coerced into a string. That means that maps are a lot more versatile than objects when it comes to storing data. For example, let's assume that you're writing a web page that needed to store data that's associated with the DOM node. You can actually use the DOM node itself as the key in the map, and then whenever you needed to access the data associated with that node, you could look it up using the DOM node itself to find the associated data. And no matter how many DOM nodes you inserted into the map, each DOM node is a unique item inside the map, and therefore, the data corresponding to each DOM node would be easy to access. Maps have a lot of the same functionality and features that Sets do. For example, you can see how large a Map is using the size property. With Map, though, you don't call an add function to add items to the Map, you call a set function. This function takes in two parameters, the key, I'll use a string in this case, and a value. Now, again, these two arguments can be objects, not just primitives. When we want to access a value from a Map we call a get function. And as I said, you can use objects as a key. So, for example, if we had a map that was associating a set of user objects with their ages, then associating a user object with an age is easily done. Like Sets, you can pass an array into the constructor for a map. Unlike Sets, we can't just pass in an array of items, we actually have to pass an array of arrays. Each of these sub-arrays needs to have two items. The first item will become the key, and the second will become the value. This will create a map with three items in it, a name, an age, and a weight. Like sets, you can use the has function to check a map to see if it already has a particular item. Here I've asked the set if it has an item with the key of the string age. That will return a Boolean, in this case indicating that yes there is an item with that key. Like sets, maps do not allow duplicate items to be entered, but with a map, when you add a duplicate item, it will replace the other item. Here I've added two items of the same key, so the test shows that the first item with the value of the string first has been overwritten by the second item, the string second. Maps also have a clear function, just like sets, which will remove all items from the map. Here I've added three items, then called clear, so the map is going to have 0 items in it. And like sets, maps have a delete function which will remove a specific item. Here I've added two items with two different keys, but I've deleted one of the items, so the has function will return false when I ask the set if that item exists. Now just like with sets, you can iterate through maps using several different methods. The first of these is the forEach function. In this case, the callback that you give it requires two parameters, the value and the key. This map has 3 items, so forEach should be called 3 times, 1 for each item, and therefore, my iterationCount variable should be 3. And just like with sets, you can use for of iteration with maps. If you provide a single variable in the for of syntax, then that variable will be an array with two elements, but using new ES6 syntax you can actually do this to get each value into its own variable. That's a lot nicer than accessing items in an array. And, of course, since we're looping over each item, this iterationCount variable is going to be 3 when the test completes. Like sets, maps expose several iterators to work with the items within the collection. The first of these is the entries iterator, and that returns an array with two items. The first is the key and the second is the value. So here I'm taking the first return value from the iterator, which is, again, an array with two elements. The first element is the key, the second is the value. And like sets, maps also expose a values iterator, which just enumerates over the values. So, of course, the first item returned by the iterator is the value of the first item, which is 'a'. And just like sets, maps expose a keys iterator, which will iterate just over the keys. Here, just like with the value iterator, the keys iterator just returns a key for each item, and the key for the first item is 1. Let's scroll this up. And the last feature we'll look at with maps is constructing maps with an iterator, which will create the map with items in the iterator. The iterator needs to return arrays with two elements. The first element will become the new key, the second will become the new value. So we've created a map with three elements. Then we have passed the entries iterator into a new maps constructor, and the new map has three elements. Now let's go to the browser and run these tests and show that they all work. Again, at this time, the only browser that fully supports all of the map functionality is the pre-released version of Firefox, but that's certainly going to change over time. And there we've seen all the functionality of maps.
WeakMap and WeakSet
In this section, we're going to talk about the WeakMap and WeakSet objects, which are variations of the Map and Set objects. Before we start looking at code, we need to understand the purpose behind the WeakMap and WeakSet and how they differ from pretty much everything else in JavaScript. So let's imagine the following scenario. We've got a DOM document that has a Body with three Divs inside of it, and we've created a set object. Now we add to that set the three Divs from that Body. That creates pointers from the Set to those three divs in the Body. Let's assume that our Set keeps track of nodes that have been processed in some way. Now at some point, something else happens and we do something that actually deletes a DOM node. So in our case we'll delete Div2 and remove it from the Body. But what happens is not that Div2 gets deleted, but instead it gets removed from the Body, but our Set object still has a pointer to that Div. Because of this, Div2 cannot be garbage collected. Even though we never intend to do anything else with Div2, it's still stuck in memory and the garbage collector is unable to recycle it. This can be a real problem for DOM nodes, because they can get extremely large, but it can be a problem for any kind of object. Once you remove an object from use inside of our program, if that object is still pointed at by any utility classes such as a Set class, then it can't be garbage collected any more, and we'll have to remember to go to the Set and remove the object from the Set. Well, WeakMaps and WeakSets were invented to solve this problem. The pointers that they have to the objects that have been added to them are not strong pointers, and therefore, the garbage collector can recycle the objects, even if the WeakMap or WeakSet is still pointing at it. Let's go and look at some code. Now the first thing that we should understand is that because the garbage collector may happen at any time, and therefore, it might remove items from a WeakSet at any moment, even in the middle of executing code that you wrote, all the functionality that deals with Sets as a collection has been removed from WeakSets. So, for example, WeakSets do not have a size property. They also don't have an entries iterator, they don't have a values iterator, and they don't have a forEach method. Furthermore, you can't iterate through them using for of, but other than that, WeakSets function just like Sets. So you can use the has function to find out if WeakSet has an item, and just like with regular sets you can delete items. We can also clear items using the clear function. Just like with regular sets this will clear out all of the items inside of the set. But when we verify that the set is now empty, because there is no size property, all that we can do is check that the item that we know about is no longer in the set. Now let's take a quick glance at WeakMaps. WeakMaps are just like WeakSets, in that they remove all the functionality that deals with maps as a collection. So, again, a WeakMap doesn't have a size property, it doesn't have an entries property, it doesn't have a keys property, it doesn't have a values property, and it doesn't have a forEach function. But just like regular maps, with WeakMaps you can do things like determine the existence of an item with has, get a value with get, remove items with delete, and remove all items with clear. Here, like with the WeakSet, I've added two items and then cleared the map, but again, I can't check to see if the map is truly empty by looking at the size property, so I have to ask if each item exists separately. So you can see that even though WeakMaps and WeakSets support most of the functionality of Maps and Sets, they still are missing some key features. So you're definitely going to need to understand the differences between the data structures in order to choose the right one for your needs.
Summary
In this module, we looked at some interesting features of ECMAScript 6. We saw the new capabilities of Number and Math, how arrays have changed, and we looked at the two new data structures in ES6, Set and Map, and also looked at how their Weak versions worked as well. All these new features give us a greatly improved capability to solve algorithmic problems in JavaScript.
Asynchronous Development in ES6
Introduction
Hello, and welcome to the module on asynchronous coding with ES6. This module is going to be one of the more difficult modules conceptually. Asynchronous coding is never simple, so you'll want to put on your thinking cap. In this module, I don't use Tracer at all, all the demos are done with Chrome, but again, by the time you watch this, other browsers may support all the functionality, so be sure to try it in your favorite browser.
Promise Primer
We'll start out by taking a quick look at promises in the abstract. This is not meant to be a comprehensive look at what promises are or how they work, but instead be a quick guide to them. The next section will look specifically at how to use ES6 promises. The most basic form of asynchronous programming with JavaScript is the callback. In this scenario, the caller spawns some kind of asynchronous operation, maybe a timer or an XHR call. When the caller initiates the asynchronous process, it passes a callback to the process, trusting that when the process finishes it will invoke the callback. At some point in the future, the asynchronous process finishes, at which point the callback is put onto the call stack, and then it will execute whenever all the other processes ahead of it on the call stack are complete. There's a few problems with this method. First, only the caller can get notified that the asynchronous process has completed. No other interested party can take an action when the process completes unless the callback itself notifies the other interested parties. Second, error handling is difficult. Also, dealing with several asynchronous processes at the same time is tough to manage. It's up to the callbacks themselves to manage this. Finally, the callback is responsible for multiple things in this scenario. It has to process the results of the async call, plus it has to kick off any other processes that want to execute based on the result of the async process. This dual responsibility is another source of pain with callbacks. Let's look at a code example. In this oversimplified example, we are looking up a company based on an orderId. So first we get the order, then when that's complete we can get the user, and then when that's complete we can get the company. Obviously, this level of nesting is undesirable. Notice how each callback has to not only process the data by calling the appropriate next function, but it also has to pass in an appropriate callback into the next function, so each callback is not only handling processing, but also flow control, and when we add exception handling it gets even worse. This may seem unnecessary, but remember that each callback goes onto the call stack separately from the caller. Because of that, any exceptions they throw aren't caught by the original initiator of the async request, so this is very much necessary. So what's the solution? Promises have long been recognized as a solution to what is often known as callback hell. A promise is an object which represents a handle to listen to the results of the async operation whether it succeeds or fails. In other words, the promise promises to alert you when the async operation is done and give you the results of that operation. One of the main benefits of promises are that they are composable. You can take two promises, which represent two different asynchronous operations and chain them together so that one happens after the other. Or you can wait for them both to run, and then run a new operation when both are complete. You can make other promises that depend on the results of a previous promise, and succeeds when it succeeds, and fails when it fails. Another benefit of promises is that instead of relying on the callback to not only process the results, but also manage flow control, we can move that responsibility to someone else who will then compose the promises in the appropriate manner. A promise is actually made up of two parts. The first part is the control of the promise. In many libraries this is called a Deferred, and it is a separate object. In other implementations, this is simply a callback itself. This gives the creator of the promise the ability to mark the promise as succeeded or failed. We'll see more about this later on. The second part is the promise itself. This object can be passed around and enables interested parties to register listeners who can take actions when the asynchronous operation completes. That way, flow control is no longer the responsibility of the same function which will handle the result of the operation, and therefore, it can focus solely on doing its job and others can worry about their own responsibilities. What's even nicer is that the promise will also notify if the async process has a failure, so error handling is no longer swallowed up into the great asynchronous abyss of JavaScript call stacks. A promise exists in one of three states, pending, fulfilled, and rejected. Pending means that it hasn't completed yet, fulfilled means it has completed successfully, and rejected means it has failed somehow. Note that the rejected state now means that we're no longer dealing with exception handling when something goes wrong, we're dealing with a promise in a rejected state, so anyone who needs to take an action when a particular asynchronous call fails can take that action and call stacks no longer get in the way and exceptions don't just silently fire off and get forgotten. Let's look at a code example. This is how our code looks using callbacks. Now let's look at the same functionality with promises. Let's just take a quick moment to look at what this code is doing. First, we call the getOrder function. This function has changed from before. Now instead of using a callback to do everything, it returns a promise. Because of that, the callback can simply worry about handling the results of the call and process the data. When getOrder returns successfully, then this callback here is executed, which calls getUser, another async operation that returns a promise. When that returns, this callback here is called, which executes getCompany, which again returns a promise, thereby calling this callback here when it returns. Notice how on each call the result of the call is passed into the next callback. GetOrder returns an order, getUser returns a user, getCompany returns a company. And finally, to handle any exceptions which are expressed by a failing promise, we call .then again, with the first parameter being undefined, and the second parameter a callback which will handle a failure. This code is significantly more readable than what we had before with all the callbacks. This is pretty generic code. There are many existing promise libraries out there, jQuery, Q, and RSVP, just to name a few, and now the promise is built-in to ES6. This code will pretty much work with any of those. So that concludes our brief primer on promises. In the next section, we'll learn how to use ES6 promises in code.
Promise Basics
In this section of the course, and for the rest of the clips inside of this module, we're going to be using the asynchronous functionality inside of Jasmine. To show you how that works, when you create a test, in the callback to the test, if you pass in a done parameter, that done parameter is a function. Jasmine will wait until that function has been executed before it considers the test to be complete, so at some point, you'll have to call done, and that will make the test complete. Jasmine will time out if that function hasn't been called after a certain amount of time. That way, asynchronous code can invoke the done method to let Jasmine know that the test has been completed, so you'll see this done parameter inside of every test that I do. That way, even if you have some asynchronous code, say, with a setTimout, Jasmine still knows the test has been completed successfully. This invocation of the done function tells Jasmine that the test is finally complete. This allows our test to involve asynchronous code, so you're going to see this done parameter inside of every test that I create, so be aware of that even if it's off the screen. The first thing you want to do is show the basics of promises and how to create and listen to them. So we'll start by creating a new test, and again, I've used the done parameter inside of this test. And now I'm going to create a new promise by creating a promise variable and then constructing a new Promise object. This Promise constructor takes in a single function which has two parameters, resolve and reject. Those two parameters are functions themselves. When we're ready to resolve our promise, we can execute the resolve function or if we need to reject our promise and cause it to fail, then we'll execute the reject function. So let's just resolve the promise. Now we can listen to this promise and execute some code when it finishes by calling promise.then and passing in a callback. Inside of this callback we'll call our done function, and this will execute the basics of a promise. We've created a new Promise and resolved it and we've added a listener to the promise by calling the then function and passing in a callback. Let's go ahead and save and we'll watch the test execute in the other window. Now that we've seen the basics of how to construct and resolve a promise and how to listen to it with the then function, let's expand upon that and look at how to deal with returned data. Quite often, asynchronous functionality involves gathering data. Therefore, promises need the ability to pass data back to the listeners and let them know that the asynchronous functionality has completed with the given data. In this case, I'm going to resolve with the value of 1. Now, when I listen to the fulfillment of that promise, I can receive the data as a parameter to my callback, and that data will be the data that's passed into resolve. In executing that test, you can see it passes as well. Now what if our asynchronous action fails? Then we can reject the promise using the reject function. To reject a promise, we simply call reject and we can either pass in an error or we can just pass in a string that is the reason why we rejected the promise. In order to take action when that promise rejects, we need to pass in two callbacks to the then function. The first one is for when it's successful, and the second callback will be when the promise rejects. Let's save and execute this test as well. Now there is a shorthand for doing this. If all we care about is taking action if the promise has failed, then we can use a different function instead of then. So I'm just going to copy this entire test, and we can do that with the catch method instead of the then method. Catch simply takes in a single callback that will only execute if the promise is rejected. So this is completely identical to the previous test, and if we save this, we'll see that that's passing as well. Now in the all the tests that we've written so far, when we resolved a promise we resolved it with a static value, but you can also resolve a promise by giving it another promise. So, for example, let's create a promise, and we'll resolve this promise with a static value of 3. Then we'll create a new promise and we'll resolve this promise with the previous promise. This is part of the composability of promises. What will actually happen is this promise will either resolve or reject based on the status of the previous promise, so since the previous promise resolved with a 3, this new promise is going to resolve with a 3, and we'll write our test around that. Now so far, we've been resolving and rejecting our promises by calling the resolve and the reject function inside of the callbacks, but there's actually a shortcut method in case we need a promise that automatically resolves or automatically rejects. So let's essentially write the same test using the shortcut method. Here, I've created a new Promise that just automatically resolves with a value of 3. Now I can create my other promise the same way by passing in the previousPromise. And now putting in the same then function, this is going to function exactly identical to the previous test. There's also a static reject method, so we'll just write a quick test showing that. And we'll use the catch method to handle just that error, and we'll save and run this test. And one last thing that I want to point out is that the callbacks given to the promises are actually executing asynchronously. Even though we're immediately resolving or rejecting our promises, that code is still executing asynchronously, basically the same as if we were to call setTimeout with a duration of 0. We can write a test to prove this. I'm going to create a variable called async and initialize it to false. Then I'll create a promise and I'll immediately resolve it. Then I'll call the then function and expect the async value to be true. Now at this point, this test will fail because the async value is only ever set to false, but down here at the bottom of the test I'm going to set the async value to true. Now when I save and run this test you can see that it's passing. That's because, as the code executes, our callback that expects the async value to be true is running asynchronously, and so it's put under the call stack and not executed in line. Instead, the last line of code where we set asynchronous to true is going to execute, and then the callback, which expects that the async value was going to be true will execute. This is an important point to be aware of with promises, that even if you resolve them immediately, they're still not running synchronously. And there we have the basics of using promises. In the next section, we'll look at some more advanced uses of promises and how to chain and parallelize them.
Advanced Promises
In this section, we're going to continue our look at promises by looking at some more advanced functionality that we can do with promises. At the beginning of this module, we saw how promises can really clean up asynchronous callbacks, so we're going to implement the same code that we saw and show how to chain promises together in order to make multiple asynchronous calls sequentially. That sample code first call getOrder, passing in an id, we'll just passing the value of 3. then we call then and we received back an order. Inside this callback we'll call getUser. Notice how I'm returning the value from getUser. GetUser is going to return a promise just like getOrder, and it's very important this callback returns this promise. That's what allows the chainability. Now if I call .then again, I'm going to be listening to the promise that's returned by this previous callback. So this promise is going to receive a user and at this point we'll call getCompany and we'll chain again so that we can get the company, and now we'll set an expectation for our test. And again, we can always put in a catch in case there's any exceptions. Now this code is not going to work because we don't have a getOrder function, a getUser function or a getCompany function, so I'm going to implement those other functions inside of the code.js file which you see open already. But before I do that, I need to add the code.js file to my runner.html file. So I'll open up my runner.html file, and down here at the bottom where I've got the script tag for my promiseSpec, I'm going to copy that, and add in the code.js file here. Now I can go into my code.js file and implement those three functions. These functions have to return promises, so I'm just going to use resolve promises. GetOrder is going to resolve with an object that has a userId, getUser is going to resolve with an object that has a companyId, and getCompany is going to resolve with an object that has the name of Pluralsight. So now I'll save that code and go back to my promiseSpec, and once I save this my test will execute and we're going to see it pass. So here we've written the exact same functionality and explained how each of these different callbacks, by returning a promise, allows promises to chain together so that the code is executed sequentially. Now what if we wanted a promise to resolve after several asynchronous operations had all been completed. Well, with ES6 promises, three's a way to do that too, and that's using the all function. Let's imagine I'm going to call to the server and gather up three courses, and then I want to wait and execute some code once all three of those courses have been returned. I'll start by creating an array of courseIds, and then I'm going to create an array of promises. Now I'm going to loop through the courseIds. Now, of course, I should be using a let keyword and not the var keyword here, but because it's not currently supported in all browsers, I'm just going to stick with var. Now for each of these courseIds, I'm going to call a method named getCourse and pass in the courseId, and this method is going to return a promise, so I'm going to push each of those promises onto the promises array. And now that I've created each of those promises, I can call the static all function on the promise object, pass in the array of promises, and this is a new promise that I can listen to calling then, and that will receive back an array of all the values that each promise was resolved with. And since there are three courses, I'm going to have three values, and at this point I could also check that each of the values was what I want, but because this is an asynchronous call, we can't rely on which order the asynchronous calls are going to resolve in, so expecting them to be in a certain order would be incorrect. So I'll just call done, and let's go implement that getCourse function. And I'll just create an object that houses the three courses. I'm just going to create a simple object that represents that course with a name property. I'll create courses for id's 2 and 3, and then I'll return a new promise which resolves with a correct course based on the courseId. And now we can go back and save and execute this test. So there's how to make a promise resolve after several other promises have all resolved. The last thing we're going to look at is how to make a promise that resolves after the very first of a set of promises resolves. So if you have several different asynchronous functions going on and you want another promise to resolve after the first one of those asynchronous functions executes, then you would use the race function. So I'm going to execute essentially the same code as in the previous test, but in this case, instead of using all, I'm going to use race. Now because that will resolve after the first of the promises in that array resolves, it's not going to be passing back a values array, instead it just passes back a single value and in our case this will be the very first course that resolves. Since every course has a name property, we can test that the object that comes back has a name property. So I'll save this test, and you can see that race resolves after the very first promise has been resolved. So there's a few advanced uses of promises in ECMAScript 6.
Basic Asynchronous Generators
In this section, we're going to look at how to use generators to make our asynchronous code more readable. Let's start off with a simple scenario. Let's say I want to log out three strings to the console, and in between each string I want to pause for half a second. It would be nice if I could write that code like this. Unfortunately, we know that this doesn't work. We would obviously implement the pause function using setTimeout, which would just immediately pass execution on to the next console.log statement. Let's go in our code.js file and implement a naïve pause function. Here's a simple pause function, but if we go back and execute this test, you can see that start, middle, and end were logged out immediately, and then the two messages that we paused for 500 _____ ms were logged out afterwards. That's because the pauses are not synchronous, they're asynchronous, so in order to get the desired result, we'd have to recode our algorithm like this, and then we'd have to change our pause function so that it took in a callback. Now if we rerun this test we're going to get the correct results, but obviously this code is not extremely readable. Thankfully, with ES6 generators there's actually a way to make this work. If you'll remember previously in this course when learning about generators, whenever we encounter a yield statement inside of a generator function, the execution is paused at that point until the yield statement returns. We can take advantage of this to fix our code and make it look the way that we want. Let's rename this pause function to oldPause, and we'll create a new test that will make use of generators. Note that this is an asynchronous test, so we're using the done parameter, where our first test was not asynchronous so there was no done parameter. The first thing I'm going to do is to define a generator function. Inside of here I'll write the code the way I want it to look, and then we'll execute the done function. Now, of course, for our pause function to actually yield up control, we have to add the yield statement, and that will allow this generator function to actually pause execution, execute this pause function, and then return control back to this function after the pause function has completed executing. Now in order for this generator function to run, somebody's got to call the generator function and call next on it the appropriate number of times. Of course, we could do that right here, and then call next the appropriate number of times, and that would execute this function, but we don't want to have to write this code everywhere we use a generator function, so instead let's encapsulate this inside of another object that will handle running asynchronous generator functions for us. I'm going to start out with an _____ if e, so that I can create some variables and not pollute the global namespace. I'll first create a sequence variable, then I'll create a function that will run the generator. We'll execute the generator and assign it to the sequence variable and then we'll create a next variable and assign it to sequence.next. Now whenever my asynchronous code like the pause function is complete, I want it to be able to notify that it's complete, so we're going to write a resume function that will allow external code to notify the generator function that it's done. And, of course, the step that it will take when it's done to resume execution of the generator function is to call next on the sequence. And now I want to put all of this inside of an object that can be accessed anywhere. So I'll add to the window object an async object which has two members, run, which will be that run function, and resume, which will be the resume function. Now we've also got to implement our pause function and we have to make it aware of this async object and let the async object know that it's done. So, again, we'll use a setTimeout, we'll log out that we've paused, and then we'll tell the async object that it can resume. And now the pause function is notifying the async object that it can resume, and since resume calls sequence.next, that will return control back to our generator function and it will continue executing. And you can see that now that I've saved and executed the test, we're actually getting the correct results. And then we can go back to our test and we just need to execute the main function by calling async.run and passing in that function, and now if we save and execute our test you can see that it's passing. Our console is a little cluttered up because both tests are running at the same time, so let's ignore the first test by adding an x in front of it, and run it again, and now we're just running the second test, and you can see that it's getting exactly the results that we want. So this code down here. It's producing exactly the same result as this code right here, but of course, the code is a ton more readable. And there's a simple example of using generators to clean up asynchronous code.
More Asynchronous Generators
In this section, we're going to continue our look at using generators to make asynchronous code more readable, but we're going to continue on and add the next logical step. In the code that we've written so far, we were able to write synchronous looking code that's actually asynchronous, but one of the things that our pause function is not doing is returning a value. In many cases, when running asynchronous code, we're actually calling up to the server, and often we're gathering up data. So in that case, we need the ability to return data from our asynchronous function calls into the generator function and be able to leverage the data returned in further statements. Let's imagine the following scenario. Let's say I'm writing a program that's going to make stock trades. So first I need to grab the price from the server, then I need to check that price. Let's say if it's over 45 then we're going to execute our trade, and then in that condition I'll execute my trade, and if the price is less than or below 45, we'll just log out that we didn't make the trade. Now, of course, we have two asynchronous operations going on here. The first is the getStockPrice call, and that returns some data, and the second is the executeTrade call, which does not return data, although it easily could. So let's turn this into a generator function. At the end of it we'll add in our call to done and we'll add in our yield statements. Now the question is, where on this line do we add in the yield statement. Well, if you'll remember, generators can actually return values when yielding, so we're going to add the yield statement after the equals sign. And when this function calls sequence.next, we're going to pass in a value, and that value will be the result of the yield statement, which will then go into the price variable, which is used in the next line of code. Now let's go into our code file and let's update our async object to support return values. Now all we have to do in order to support return values is to add a value parameter to this resume function and pass that same parameter into the next function. Now, any code that calls resume, if it passes in a value, that will be the return value of the yield statement that it belongs to. So in order for our code to work, we have to implement this getStockPrice function and this executeTrade function. So back in our code.js, we'll implement those two functions. Now at this point, we would execute an xhr request in order to gather the data from the server, and we would possibly use something like jQuery to make that request, and that might look something like this. Unfortunately, since we're using the file protocol to run our tests, making an xhr call isn't going to work, we'd get a security error, so I'm just going to write a little fake function that will mimic jQuery's get request. So instead, I'm just going to use setTimeout to simulate the asynchronous nature of an xhr request, but realize that this should be an xhr call. And we'll use the price of 50, and we'll have this timeout for 300 milliseconds. We also need to implement our executeTrade function, and I'll use setTimeout again. And inside of here, I'll log out that the trade is completed, and I'll call async.resume, but with no value passed in, and again, a 300 millisecond timeout. Now at this point, we can save and execute our test. We just need to add the call to async.run, close up the test, and if we save it, it will execute. So you can see the test is passing and we got the console message that the trade is completed. So now we've been able to successfully modify our asynchronous generator runner to allow for values to be passed around. Now what if we get some kind of an error? How can we handle that? Well, let's create a new test that will be a copy of the existing test. We'll change the name. And what I'd like to do is wrap a single try catch block around all of my code inside of my main function and be able to handle any error that was generated, even by an asynchronous call. Now unfortunately, currently our code doesn't support that. If we go into our code.js and go up here into getStockPrice and throw an exception, that exception is going to get swallowed up. And we can see in our console we've got an exception, but the exception isn't showing up anywhere. And you can see that after a timeout the tests are failing. So in order to support this, we're going to go back into our async object and we're going to add one more function, and this is going to be a fail function. In sequences, in addition to having a next function we also have a throw function, and when you call it, it will act as if the yield statement itself threw an exception. So this is very similar to how calling next causes the yield statement to return a value, except it causes the yield statement to throw an exception. So now we need to add this fail function as a method on the async object, and we can go back down to our getStockPrice function, and we'll wrap this entire setTimeout inside of a try catch block. And inside of here we'll call async.fail, and now any of these asynchronous functions, if they have an error, can simply call async.fail, which will re-throw the error in the generator function. Let's go back into our test and let's comment out the other tests so they don't clutter up the console, and they don't fail since they aren't handling errors, and let's save and run the test. And you can see that the test is passing, but the console is logging out that it caught an exception. Now let's go up and remove this thrown error so that the other test will work again. And there we've seen how to use generator functions with return values and trapped exceptions. In the next section, we'll take one more step with our generator functions and clean them up even more.
Asynchronous Generators and Promises
In this last section, we're going to finish up our look at asynchronous generators by learning how to pair them up with promises. And one of the main drawbacks of the implementation that we've done so far, is that all of our asynchronous functions have to be aware that they're being called inside of an asynchronous generator, and therefore have to be able to call async.resume in order to resume our async.fill. For example, here in our getStockPrice function we've got to call resume as soon as our setTimeout completes, and we've got to call fail if we catch any exceptions. This means that this function cannot be used outside of a generator. We'd like to fix that issue by pairing up our generators with promises. So the first thing I'm going to do is come up and copy this test and I'm also going to ignore it, and let's make a new version of this test. We'll change the name. Now I want to leave the other test functioning, so instead of changing the code that we've already written in order to work with promises, I'm going to create a promise-aware version of each of these functions and a promise-aware version of our async object. So I'm just going to suffix these two functions with P, and I'll do the same thing with our async. Then we'll go back into our code and we'll create promise-aware versions. So I'm going to copy my entire async if e and create a new version. This will be asyncP. Now, because we're going to be using promises, the promises themselves will be the notification to the asyncP object as to whether or not they are done or whether or not they failed, because the asyncP object itself can listen to the promise and take an action when the promise either resolves or rejects. Therefore, we're not going to need our fail or resume anymore, so I'm going to cut those out, and I'll take them off the asyncP object. But we've got to make some major changes to our run function. We'll no longer need the sequence variable, and these two lines of code no longer apply. Instead we're going to start over. I'm going to create a new inner function called process and that will take in a result. That result is going to be the result of a call to generator.next. You remember that calling sequence.next produces an object that has a value and a done property, so we're going to grab the value property and that's going to be a promise. So I'm going to call then on it, and when the promise resolves correctly, which will be this callback, then I can ask the sequence if it's done, then we're going to run a process again on the sequence. So we do need a sequence variable, but this one can be inside of our run function. So I'm going to go up here and create a sequence variable, but I'm not going to initialize it yet. Then inside the call to process I'm going to call sequence.next and I'm going to pass in a value. That value will be the parameter received by my callback right here. Remember, this callback receives the value that the promise resolves with. So whatever the promise resolves with is going to be the value of the sequence.next call, which means if the yield statement is used as a return value, then whatever the promise resolves with will be what is returned by the yield statement. Now I'm going to initialize this sequence variable by executing the generator itself. Now remember, even though I'm using sequence right here, this is actually inside of a function, the process function, and the process function hasn't been executed yet, it's just been declared. So even though we're initializing sequence down here, it's okay because sequence hasn't actually been utilized yet. Now that we've got our sequence, we'll call next on it, and then we'll process the next variable. Now remember when we first talked about callbacks at the very beginning of this module, we talked about the fact that one of the big drawbacks to callbacks is that they can't notify other interested parties that they have executed, but promises fix that problem. As long as you have a handle to a promise, you can ask that promise to notify you when it's done by calling the then function. So we've taken advantage of that here in our asyncP object so that any asynchronous process that is represented by a promise can be part of an asynchronous generator, and this controlling object will just listen to the promise, and when it's complete, it will call next on the sequence. And that process of listening to a process and then calling next will continue on until there are no more promises or asynchronous functions left to call inside of the asynchronous generator. Now let's go down and create our promise-aware versions of our two asynchronous functions. First we'll create the getStockPriceP function. This is going to return a promise, and then we'll call setTimeout, and inside of the setTimeout we'll resolve with the value 50 and we'll delay for 300 _____ ms. Now our executeTradeP function, essentially the same algorithm, and now we've got promise-aware versions of these functions. And what's great about these versions is that these could be used under any circumstances. They don't have to be used inside of an asynchronous generator, they could be used by any code. And no matter how they function or what third party library they use in order to make their XHR call, once the XHR call completes, as long they call resolve, then the asynchronous generator will know that they're done and continue on with next. Now let's save our code here, and we'll go back to our test and save that code, which will comment out the previous test and execute this next test, and you can see that the test is passing. Now let's do one more thing and enable this to work with any rejected promises. Let's go back into our executeTradeP, and instead of resolving, we're going to reject. And we'll comment out our call to resolve, and we'll go back up to our asyncP object and we'll add in one more thing. After our first callback to then, we'll add in a second callback, which will be if the promise rejects and we'll add in the same logic, except instead of calling next we're going to call throw. And now any value that a promise rejects with is going to be thrown inside of our containing function, and that way the containing function can handle the error with this try catch block. So when I save this code and execute the test, we'll see a comment that indicates that the error was trapped. And there we go, we've now got our asynchronous generator running with promises, and this is a much cleaner implementation than the previous implementation, because all of our contributing functions don't have to be aware that they're being run inside of an asynchronous generator, they just have to implement promises, which, since they're asynchronous, it's a good idea to do that anyway.
Summary
In this module, we saw how the new ES6 promises work. Then things got crazy when we saw how to use asynchronous generators to create asynchronous code that looked like synchronous code. And then finally, we got really freaky when we put promises and generators together, but the result was some pretty nice-looking code that was a lot more readable than any other alternative for asynchronous code. Regardless of their complexity, the power and elegance of promises and asynchronous generators just can't be denied.
Objects in ES6
Introduction
Hello, I'm Joe Eames. In this module, we'll be looking at the changes to object in ES6. We'll be focusing on three areas, two new functions on object, creating object literals, and using proxies to intercept operations on objects. The two new functions we'll look at are the is function, which is an alternative for === and handles a few edge cases differently, and the assign function, which is a built-in replacement for the very common extend function that you'll find in libraries like jQuery or underscore for adding functionality through mixins to existing objects. After that, we'll look at some new features in shorthand syntax when declaring object literals. Then we'll follow that up by looking at the new proxy object, which lets us do things like listen to an object and intercept whenever a property is read or updated, and many other operations. Because of the differing support for these two features, we'll start out this module by using tracer when looking at the new functions, and object literals. When we move onto proxies, we'll drop tracer and just use Firefox Nightly, since currently that's about the only environment that supports proxies.
Object.is() and Object.assign()
In this section, we're going to learn about two new functions that have been added to object, and those are the is function and the assign function. Let's start with the is function. The is function is very similar to ===, but it was added to deal with a couple of pieces of quirky behavior in the === function. You can use Object.is anywhere that you would use ===, but unlike ===, it's not an operator, it's a function. It's extremely straightforward to use, you just call Object.is and pass in two parameters. These parameters will be compared to each other, and if they're the same, then Object.is will return true, if they're different, Object.is will return false. In almost every case, it works exactly identical to triple equals. But as I said before, there are two cases where === operates a little strange. The first case is with -0 and +0. Triple equals considers those two values to be the same. JavaScript represents numbers using assigned number system. That means that one of the bits that represents each number is a sign. One of the strange effects of that behavior is that 0 has two representations, that's 0 with a positive sign and 0 with a negative sign. In JavaScript, you write positive 0 as just 0, and negative 0 as negative 0. Now, if we were to do this, then === is going to return true, because with === those two values are identical. Object.is allows us to differentiate between the two. You can see that the way that we call Object.is is to pass in two parameters. Those parameters are going to be compared against each other, and if they are the same, Object.is will return true, and if not, it will return false. So in this case, Object.is is going to return false. Let's save and run this test. Now this scenario is a little bit strange. It's not too often that you're going to need to compare negative 0 and positive 0 and need them to return as different values. But the second case where Object.is differentiates from === is slightly more common, and that is with the NaN value. So if we do the following, common sense would dictate that this would be true, but the actual answer is false. Let's save and run this test to show that it's correct. You can see that === considers NaN to be different than NaN. Object.is deals correctly with this scenario. In this case, using Object.is to compare Nan to NaN will return true. Let's save and run that test again, and you can see that the test is still passing. So depending on your comparison needs, Object.is is an alternative to using ===, and it also deals with a couple strange edge cases better than === does. The other function that we're going to look at is the assign function. Nearly every general purpose framework or library in JavaScript has had some form of a function that will allow you to take properties from one object and copy it onto another object. In jQuery and Underscore, this is the extend function. This allows you take an object called the target object, and using a source object, copy the properties and methods from the source object onto the target object. That capability has been added to ECMAScript 6 using the assign function. Let's go through an example. In many scenarios and in other languages, this functionality is often described as mixins. So we'll create a new test and say that the assign function should apply mixins to objects. Let's say that we have a shark object, and that shark object has one behavior, a bite function. The bite function sets the hurt property on the target object to be true. For our purposes, we'll also need a sample target object, so we'll create an object called person, and that will just be an empty object. Now let's say we want to give our shark objects some new capabilities. Somewhere else in our system, we've got a laser mixin. This object also has one behavior. Now if I'd like to assign the laser mixin to the shark object, I can use Object.assign. I call Object.assign, first I pass in my target object, which will be my shark object, and then I pass in the mixin. Now if Object.assign, just as with the extend method in jQuery or Underscore or other similar methods from other frameworks, you're not limited to just one source mixin, you can actually add in multiple mixins here and each one would be applied to the shark object. So let's go back to our single mixin, and now that we've assigned the laser functionality to the shark, our shark has a new behavior. And at this point, we can test that the correct result happened to our person object. Let's save and run that test. And you can see how the assign object allows us to give new behaviors to existing objects. Now it's important to realize that there's not some kind of forwarding going on behind the scenes. In our scenario here, the pewpew function was actually added directly to the shark object. It now has its own pewpew function. Calling that function on shark doesn't forward it onto the laser object, it actually calls it on the shark object. So you can see that Object.assign allows us to give new functionality to existing objects.
Object Shorthand and Computed Properties
In this section, we're going to look at a few new pieces of syntax that have been added to ECMAScript 6 when creating object literals. The first one is the property initializer shorthand. When creating a new object literal, we are often assigning properties based on local variables. So for example, when creating a car object, we might have a couple of local variables, model, and year. Now in creating a new object literal based on these values, if this new object had a model property that was going to be assigned the value of the model local variable, we would do this, and if we needed to do the same thing with year, what we would end up with is an object that looked like this. This sort of thing is something that's done all the time. With ECMAScript 6, a new shorthand syntax has been added to make this a little bit easier. When the two identifiers for both the property and the value are the same, we can actually skip the duplication and just do this. This will produce identical results to the syntax we just saw, and we can prove that with this test. Let's go ahead and save and run it. So you can see that this allows us to create objects with properties the same as the names of local variables using a shorthand syntax. The next syntax we'll look at is the method initializer shorthand. This syntax allows us to create methods on objects using a shorthand. So, for example, if we had a server object, and that server object had a getPort function, this is how we would define it using ECMAScript 5. But with ECMAScript 6, we can remove a little bit of redundancy. This is completely identical to the syntax that we just saw, so let's create a test around it by having this function return a value, and we'll save and run that test, and you can see that this allows us to quickly and easily create methods on objects. And we have one more new object literal syntax we're going to look at, and that is computer property names. Computer property names are a way to support more functionality when defining the property names on an object literal. Let's say, for example, that we wanted a function that created a simple object. The simple object that we're going to create is going to have one property, and that property name is going to be configurable, so we'll pass it in, and the value itself will also be configurable. Now in order to create this object, we would do something like this in ECMAScript 5. And, of course, that works just great. But in ECMAScript 6, there's some enhanced syntax designed around doing this exact scenario. With ECMAScript 6, we can actually use variables when creating the names of properties in object literals. So we can turn this into this. Notice that the property name has been surrounded by square brackets. That tells the JavaScript parser that the property name is actually an expression and variables are valid inside of these expressions. Let's finish off our test. We'll create an object with a color, and the value of that color property is going to be red. And we'll test if that object's color property is red. We'll save and run, and you can see that the test has passed. This functionality being able to use expressions as property names gives us even more flexibility. We can actually do string concatenation inside of property names. So, for example, if I needed a function that would create a triumvirate object, triumvirates are a council of three people, we could return a new object, which has three properties, each property name will be the word member, and _ and then the name of that person, and the value will be the person itself. Now if we have three people objects, and we create a triumvirate using these three people, then this triumvirate will now have three properties, member_Joe, member_Ralph, and member_Harry. And, of course, the value of this property is going to be the object Joe. Let's save and run that test. So you can see that with the new syntax for computer property names, we have a lot more freedom when creating property names on objects using a much easier syntax than we had in ECMAScript 5. Between that and the shorthand for defining properties and methods, we now have a lot easier time creating object literals.
Proxies
This is going to be our first of two sections about proxies. Unlike the previous sections, we're no longer going to be using tracer in order to compile our ES6 code down to ES5. Currently, proxies are not supported by tracer. They're only supported by Firefox Nightly. Again, as times goes by, more and more environments will support proxies, so you may find that your current browser supports it. If you're following along, you can use the non-tracer skeleton project available in the course's GitHub repo. Proxies themselves are very interesting, because they allow us to intercept operations done on objects. The most straightforward operations to demo are intercepting gets and sets on properties, so we'll start by looking at how to intercept gets of properties on objects. Let's say I have a unicorn object. This object has three properties, the number of legs, its color, and a Boolean indicating whether or not it has a horn. Now let's assume that we want to make sure that whenever anyone asks unicorn for its color, instead of just returning the color by itself, it actually returns out the word awesome and the color, like this, since unicorns wouldn't be just a plain brown, they'd be an awesome brown. But we can intercept get requests by using a proxy object. Now when we use a proxy object, we're not modifying the original object itself, instead we're creating a new object that's a wrapper around the original object. So if you're going to utilize proxies, you have to access the object through the proxy and not through the original object itself. Let's create our proxy object. We do this by creating a new proxy. This takes in two parameters, the original object itself, often called the target, and then an object literal, which represents which operations we want to handle. In our case, we want to handle get, so I'm going to create a key called get, and that will be a function, which takes in two parameters, the target object, which in this case is going to be the unicorn object, and a string parameter, which is the name of the property being accessed. At this point, I can run any logic that I want, but ultimately, if I want whoever is asking for that property to get a return value, I'll have to return something. So, for example, if when people access the color I always wanted to return white, then no matter what the color of the unicorn is set to, when somebody asks for the color, they're going to see the value white. But in our case, I don't want to return white, I want to return awesome, and the actual property value itself. But I only want to do it if they're asking for the color. So I'll put in an if clause and make sure that they're asking for the color, and if so, I'm going to return the word awesome, and the current value of the color property, which I access by calling target, and then using square brackets, the name of the property. Now I also need to handle the case when they're not accessing color, so I'm going to put in an else clause, and in this case, I'll just return the property they're asking for. Now let's write some expectations to make sure that this works. First we'll make sure that accessing other properties still works, and then we'll make sure that when we ask for the color, we get the expected value. And that is the string awesome brown. Now let's save and run our test, and you can see that our test is passing. We are indeed getting an unmodified legs property, but when we ask for color, we're getting the string awesome and then the color. And we can also handle set operations on properties, and we'll go with a similar setup, we'll have our unicorn object with the same three properties, and we'll create our proxy unicorn, and this time we'll intercept the set operation. This function takes in three parameters, the target object, the name of the property, and the value that it's going to be set to. Now let's say that for our unicorn, we want to make sure that anytime somebody tries to set the value of horn to false that we don't allow it, because the unicorn is never without a horn. So let's check if they're setting the horn property and see if they're setting it to false, and if so, we're going to log out a message that you can't do that, and I'm not going to do anything else inside of this if clause. Because of that the value of the property isn't going to actually be changed. Since I'm intercepting set requests, if I want to actually allow the value of the property to be changed, I've got to write that code. Now in setting other properties, we still want to allow that to happen, so I'll add an else clause, and in this case, I'll just do the default behavior, which is to set the property to be the value given. Now let's actually test this. We'll start out by setting the proxy unicorn's color to white, and then we'll set its horn to false. Now remember again I'm setting the properties on the proxy, not on the original unicorn object. If I were to bypass the proxy and just operate on the unicorn object itself, then the interception will no longer happen. So we can expect that color has been changed to white, and we can expect that the horn property is still true. Now before I run this test, I'm going to double-check my code, and I can see that I've misspelled unicorn in proxy unicorn, so I'll fix that, and now we can run our test. And you can see that we're now intercepting sets correctly. Using our proxy, we're not able to change the value horn to false. Now in addition to gets and sets, you can intercept a lot of other operations on objects. For example, you can intercept calls to delete properties, calls to the define property function, calls to freeze or seal an object, you can intercept when the in operator is used, you can intercept calls to has on property. You can also intercept a numeration using for in. Also, if you need to intercept more than one function on an object, you can simply add another key value pair on the handler object. So, for example, on my proxy unicorn, if I wanted to handle sets and gets, I can simply add in a get handler like this. And now I'm intercepting sets and gets on this object. In the next section, we'll look at how to intercept function invocations. Proxies are a really versatile features that allow for a lot of things, such as incorporating validation, or even making properties be strictly typed. There are virtually no limits to what you can do with proxies.
Proxying Functions
The last thing we're going to look at in this module is how to intercept function calls using proxies. Let's start out by creating our test, and we'll create our unicorn object again. We'll go with the same three properties, but this time let's add a method to our unicorn. Let's give him a hornAttack. Our implementation is very simple, we just return a string indicating that the target was obliterated. Now let's assume we also have a thief object. This thief object has the capability of stealing methods from other objects and using them as a zone. At this point, we could call thief.attack, and the hornAttack method now belongs to a thief object. Let's say we want to prevent this from happening. We can do this with a proxy. You might think that in order to do this, what we'll want to do is create a proxy around unicorn, but that actually isn't the case. Remember in JavaScript functions are objects, so what we need to do is create a proxy around the hornAttack function, which intercepts the invocation of that function. So we're going to do that by saying that unicorn.hornAttack is now a proxy based on the original unicorn.hornAttack, and our second parameter will be our handler object. So again, you can see I'm hiding the original object, which is the hornAttack function inside of a proxy. At this point, the original hornAttack function is no longer accessible. Unicorn.hornAttack is now the proxy object and not the original function. Now in order to intercept calls to that function, we use the apply key and the value is a function with three arguments. The first is the target object, which would be the function itself. The second is the context, which is the object that is the current this for the function invocation. So down here on line 59, when we call .attack, the context would be the thief object. If we were to call unicorn.hornAttack, the context would be the unicorn object. And the third parameter is the arguments to the function. This is an array. Now in our handler, we want to make sure that we're only invoking the function using the unicorn as the context. So we'll check the context, and if it's not the unicorn, then we'll return a string that nobody can use the unicorn horn but the unicorn itself. Now, of course, we also need to include an else statement in case the context is the unicorn, and in this case, we'll just invoke the function normally and return the result. So we're going to call target.apply. Remember, target is the original wrapped function, and we'll pass in the context as the first argument so that the context will be the this object of the function, and we'll pass in the args as the second object, so that it's given the right arguments. Now when our thief tries to steal the unicorn's horn attack, it's not going to work. So let's write an expectation around that, and let's also check that the unicorn itself can still use its hornAttack. Now let's save and run our test, and you can see that we're successfully intercepting the function invocation and able to manipulate it how we want. So you can see that proxies are extremely versatile and give you a lot of ability to do some metaprogramming against existing objects.
Summary
In this module, we saw how ES6 is making quite a few changes to object. We saw the Object.is function, which is an alternative to ===, we saw Object.assign, which allows us to extend our existing object using mixins, and we saw shorthand properties and methods, which allow us to more easily create object literals. We also looked at computed property names, which gives us more options when creating property names and object literals. Finally, we looked at proxies, which allow us to intercept operations done to objects, so that we can intercept gets and sets on properties, function invocation, and lots of other operations. Through this module, we have seen that ES6 is adding a lot of new capabilities to object.
Modules
Introduction
Hi, this is Scott Allen, and this video module is about JavaScript modules. Just like a video module organizes a collection of video clips into a single place for consumption, JavaScript modules give us the ability to organize JavaScript code into a single unit for other pieces of code to consume. Modules also give us the ability to hide information by not publicly exposing implementation details and avoiding global variables. These capabilities are something we JavaScript programmers have needed for years. We've needed them so much that we've been using other patterns and frameworks to make up for the lack of a module system in the JavaScript language. And in order to understand and use this new module system, we do need to know about the current state of packaging JavaScript code. So let's start off by looking at what we've been using.
An IIFE
One coding pattern I've been using heavily in JavaScript for a few years is known as the module pattern. There are quite a few variants of how to write the code for the module pattern, but one approach is to use an immediately invoked function expression, an IIFE, like the one shown here on the screen. This is an anonymous function with parentheses applied, so the function will execute as soon as the runtime reaches the bottom of the declaration. There are quite a few desirable qualities about this code. First, I won't have any global variables defined by this code, as long as I use the var keyword properly, because by default everything inside the IIFE is scoped to the function. Employee, by default, only lives inside the function, but the second desirable quality is that I can choose to expose specific APIs or functions or objects from the IIFE if I explicitly push these public pieces that I need to the outside. For example, I could attach an employee function to the window object in a browser and that would make employee available from anywhere. But the third quality is that it is easy to have private implementation details in an IIFE, like the privateDoWork function. It's only available from inside the closure form by this IIFE, and I don't expose the function anywhere else. So what we see on the screen represents the primary goals of any module system. Number one is that a module system should let me organize code. I can put a group of related concepts into a module and treat everything as a unit, because when you use that module you get everything or nothing, it's relatively atomic. And the second goal of a module system would be to control visibility. There are some objects and functions in a module that I want clients to use, and then there are the implementation details that I want to hide. The desire to organize code and control the visibility of objects and functions has led to two unofficial standards in the JavaScript community, CommonJS modules and AMD modules. Let's talk briefly about these two.
Common JS
Back in 2009, JavaScript finally hit a critical mass, and it was moving from being a language that you would probably only use in a web browser towards being a language that you could definitely use outside the browser, on the server, or anywhere, thanks to JavaScript runtimes like V8 and SpiderMonkey and platforms like NodeJS. It became readily apparent back then that in order to manage the complexity of large applications, there was a need to organize code and control the visibility of code, and avoid global objects, just what we want from a module system. And there was a project formed, a CommonJS project, the project's goal was to promote community standards for JavaScript, and one of the standards they worked out was a module standard, what we call the CommonJS module. This module system revolves primarily around two special objects, exports and require. If we look at some of the code that we would write for our CommonJS module, first you'll notice that there is no IIFE required. That's one nice feature of having a proper module system. The module system will execute the script file inside of a nonglobal context, so I can write scripts code without the syntax and the clutter of an IIFE, which is a bit ugly. This code is self contained and isolated as if it was inside of an IIFE. I need to use the exports object to publish an API and make code publicly available. One way to do that is by attaching properties to that export object. So in this code, I'm making the employee constructor function available to the outside world simply by assigning it to a new attribute of exports. Then, in another script file I can access the exports of that module using a require function. Require needs me to specify essentially the path and the file name of that module without the .js extension, and the module system behind the scenes will go out and load that module, will cache that module, and will give me back the export object in return. In this code, I'm requiring that module and then saving off the employee function, and then using that function to create an object. Employee was the only export from that module, but you can export as many things as you want from a module. I don't want to go into the details of CommonJS; I just want to point out that it is an existing standard, not by ECMAScript, but it is wildly popular. It allows me to organize code into modules, and that means the modules can be developed independently. It controls the scope of my code and allows me to share and publish library and framework code without creating any globals. I can still hide implementation details like a privateDoWork function, because that function is not exported from the module. And to take a step back and think conceptually, CommonJS relies on exports and imports, exports with the exports object, and imports with require. This is a common theme throughout all the module systems. Let's look at another one, AMD.
AMD
CommonJS tends to focus on building standards for JavaScript running outside of a web browser, partially because the standards surrounding a web browser are already defined by well-established organizations, but also because JavaScript in the browser is such a different animal than JavaScript in an environment like NodeJS or MongoDB. Not that the language is any different, but the environment is very different, and the infrastructure is very different. And because the CommonJS modules were better suited for non-browser environments where there is a filesystem and the filesystem is fast and we don't worry about blocking a UI thread that paints the screen, because of all that, client-side developers eventually put forth a standard for an asynchronous module definition API, the AMD API. This API takes into account that we need to load scripts asynchronously in a web app and that on the web at runtime, we also need to optimize script loading so that we have fewer and smaller downloads. But during development of that web application, we want to use multiple files as modules to organize code and to develop components independently and check those components into source control as individual units, and also, control what we publish and hide inside of each file. The popular implementation of AMD is provided by require.js. Require.js is a script loader that implements that AMD API and that API primarily consists of a define function. So inside of a script file, I can define a module by providing a callback function to be executed by the script loader. This function needs to return a value or an object that I want to return as a public member, the code on the screen is again defining an employee constructor function, and it is the only export, but I could also return an object with many public attributes for our client to interact with. Now this is very similar to using an IIFE, because inside of the function, I can keep implementation details private, they are inside the function scope, the can be wrapped by a closure; the primary difference between this and an IIFE is that an IIFE executes immediately. It typically needs then some sort of global object to export public symbols. With AMD, this function executes when the script loader says it's time to execute, and the exports are in a return value. When some other module, some other script file, needs to use that employee constructor function, they will also use define to set up their module function, but also pass in an array that describes the imports that are required for this module. In the AMD vocabulary, we would say these are the dependencies. In this case, I only have 1 dependency, which is employee, but I can have 2 or 3 or 12 dependencies listed there. AMD knows how to take that string identifier and map it to a module that it will need to load over HTTP, and it will analyze the dependencies in that array, make sure they are loaded, and that the module functions are invoked and the exports are ready to go, and then it can invoke my function and pass in each dependency as a parameter, so I can receive the employee constructor function, I can invoke the employee constructor function, and then do any of the work that I need to do with that employee. Now there's lots of various tools for AMD that allow you to optimize script loading and bundle modules into a single download, but conceptually, again we have the same principle of exports and imports. Just with AMD, the syntax is set up to favor a browser environment where the script loading and execution is inherently different. So now that we've had a brief introduction to IIFE modules, CommonJS modules, and the AMD API, let me tell you why all these topics are important.
ES6 Modules
There are two reasons that I've been talking about CommonJS and AMD. One reason is that conceptually, ECMAScript 6 modules are very similar to these other two systems. We can select things that we want to export from one module and import things that we need into another module. And this is what the ECMAScript module syntax looks like. There is an export keyword. I can use this to export as many symbols as I like from a module or a module can be a JavaScript file, so this code might be inside of a file called employee.js. And I'm exporting an employee class, but I could also export another two classes and an object and a function object and a string value, but it's also reasonable to have one export per file if you have one abstraction per file. But then from a second module, I can import what I need using the import keyword. The module that I want to import from is specified as a file path without the .js extension, and you might remember the destructuring assignment that we looked at earlier in the course, I effectively have a destructuring to save the employee export into a variable named Employee, and that will allow me to use this function to construct an object by saying new Employee. So what we have really leans more towards the CommonJS standard than it does AMD. For example, that employee export that would be inside of an employee.js file, I do not need to wrap that in a function, or an IIFE. Everything that will be in a JavaScript file that is treated as a module with exports will implicitly be executed inside of some sort of local context, so that I don't accidentally create global objects. And also the import, it's very explicit, it looks like the type of import that would need to block to go out and load a script, es6 employee, before the rest of the script could continue. So that's one reason I wanted to introduce you to CommonJS, because what we have in the ECMA6 standard I think is very similar to the CommonJS specification, but there's another reason that I showed you this technology. It's because those standards, CommonJS and AMD, even though they are not the ECMAScript standard, they're not going to go away anytime soon. The ECMAScript 6 module standard is an attempt to have a single module API that everyone can build to, but it's the one part of the ECMAScript 6 specification that has undergone a lot of turn, it's the last piece to undergo changes, and it's going to take some time for that standard to take hold and to replace these other well-established standards. It's also going to take some time to get module loading capability into a browser, because that's an entirely different specifications and standards process. Now we're looking at a horizon of maybe two years or more from the date of when I'm recording this. So what do we do in the meantime? Well, I want to focus on using modules from the browser since Node already has an established module syntax. I will show you how to use traceur as a module loader, because it's very simple, but I also want to show you how to use traceur to convert ECMAScript 6 modules into AMD style modules, because that's probably what we will be doing for a few years. We're going to rely on require.js as a script loader until that day when most of the browsers that we support can load ECMAScript modules natively. So with that in mind, let's take a look.
Our First Module
Let's write our first module and get it loaded into a web page. Inside of a file employee.js, I want to write a class that will represent an employee. We'll give this class a constructor that you pass a name, and then I can save that name in the object. I can provide a getter for this name, something that returns this._name, and also some sort of interesting method that returns a result. If you ask an employee to do work, then what we could do is return this.name is working. And because this .js file will be used as a module, if I want to be able to use Employee from outside of this file, I'll need to add the export keyword. And we could have multiple exports; we'll come back and look at that topic in just a bit; for now, I just want to be able to use this class from default.html. So let me save that file. And in my HTML file, I have traceur loaded. I have a little bit of bootstrapping code for traceur. What the WebPageTranscoder will do is go out and find scripts with the type of module, and it will make sure that it will compile and execute the code inside, but what I should be able to do now is use ES6 syntax and say I want to import something named Employee from, and then I have to specify the module, it is in the es6 folder, it's called Employee.js, but I don't need to specify the file extension. And yes, this is an object destructuring, because when I export multiple things from a module, I'll need to specify which specific things that I want to import. And actually, there's a couple different variations that are very flexible there, but for right now, this is the syntax I have to use, and I have to be explicit and say that I want something named employee from that module. And once I have that, I should be able to use this thing called Employee and use it to instantiate an object, an Employee object, which we'll give a name, and then let's just do a document.write on e.doWork, and save my HTML file, and we have a result. And this is the essence of modules. I now have the ability to build abstractions, like an Employee class, package that class into a module, export the class, and use that from another piece of script code by importing from that module and not using any globals. As a next step, let's try to export a few different types of things from that module.
Multiple Exports
From inside a module, I can export many different types. Of course, they should be related somehow, but if I wanted to, I could export a function object by saying something like log = function. Let's assume that this function is dedicated to logging employee information, and something simple might be to just log the employee's name. I could also export primitives. So if I have some sort of defaultRaise percentage, I could export 0.03 as the default amount to increment a salary by, and I could also export objects. So I could create a modelEmployee and say that that is an Employee whose name is Allen. I'll save this file now how to be consumed these different exports from default.html. Thanks to object destructuring, I can grab exactly what I want from up here. So I could grab the log export and put that into a new variable log. I could grab modelEmployee, and I could continue listing things that I want to import from that module. At this point, I should be able to log modelEmployee and save my HTML file, and we are now logging that employee's name. What happens if I get something wrong? For instance, if I have a typo in this list, and try to grab something that is not an export from that module, I should be able to get some sort of error saying that what I'm asking for is not available. So you can think of the import very much like a variable declaration, but even if I only need one thing, for instance, if I only need the employee, I still need the curly braces to ask for something specific, it is a destructuring of a module object that comes back from this import. And I'm asking for the employee piece of that module object. Let's look at a couple different ways to do an import next.
One and Many Exports
Here are a few variations on what we've just tried. First, if you only have a single export from a module, you can use the default keyword and allow someone to import your export without using a destructuring. In other words, if the Employee class was my only export and it's marked as default, like it is with the code on the screen, then I can import the Employee module and create a variable named anything, in this case I just happened to name the variable factory, but this variable will hold the default export. So with the default export, I don't have to use a destructuring where the name of the variable is significant, I can name the variable anything, and I will always receive the default. You can still have multiple exports from a module with a default export, but my personal feeling is that it's probably best to go with only the single export, which is the default, unless there's a good reason to do otherwise. This makes it easier for clients to consume the module, and the syntax was really designed for those libraries like jQuery that typically offer all of their functionality through a single object. Now, if you do want everything that a module exports, you can also use the syntax shown here. I'm asking for all of the exports to be referenced by a variable employees, and so each export will be available as a property from that employees variable. If I want to get to the Employee class to construct an object, I can ask for employees.Employee. Let's try this out. In the employee module, let's pretend for a moment that I didn't have all of these exports, that I only had a single export, and that was the Employee class, I might choose to make that my default export. And if I save this file, you'll notice the web page has problems, because I no longer have access to this Employee export. It is now referenced as default, but I can choose any variable name to import that particular abstraction from the module. At this point, I'll no longer have access to log or model employee unless I went back to a destructuring assignment, and now Employee is really known as factory, and if I save all my files, the page is working again. In this case, however, I do have multiple exports. I don't want to use the default here. And that means that when someone imports this module, they even need to pick the specific pieces that they want by listing them in a destructuring assignment like this where the name of the variable will match up with the name that is exported, or I can use the module syntax that we looked at, and in this case, I'll just call the module m so it's easy to work with, now everything is referenced through that m variable. So I can say m.log, m.modelEmployee, and we no longer have a factory, but we do have an m.Employee that I should be able to use, and so we can see a little bit of what's happening. Let me also do a console.log of m, and I will save all my files. The page is back to working, and look at the m object that we got back, you can see that it has getters for getting to the employee, the defaultRaise export, the log export, and the modelEmployee export. So this is a good approach to use when you're using a module that has many exports and you need access to almost everything from that module.
Hiding the Details
One of the features that we want from a module system is the ability to hide implementation details, and this is easy with ES6 modules. All I need to do is not use the export keyword when I declare a class or variable. Someone who imports the module on the screen will have no access to privateFunction, unless I add that function to some exported object. But what about something like an employee's name? In the class on the screen, I'm trying to hide the name of an employee in only exposing a getter to retrieve the name, but anyone can walk up to an employee object and fiddle with _name to change the name of an employee. It turns out that modules and symbols can work together to prevent this scenario; let's see how. Inside my HTML file, I can walk up to an employee and change the name of that employee by modifying _name, and if _name is something that I'm really concerned about hiding inside of this object, that's something I can do by combining modules and symbols. So you might remember that we talked about symbols earlier in the course when we talked about iterators, because to get to the magic iterator function of an iterable object, I had to use an iterator property that was hanging off of symbol. And as I described symbols back then, symbol is a new built-in type in JavaScript, I can invoke symbol to give it a new symbol value. Every symbol value is unique, and every symbol value can be used as a key on an object. So one use case of symbols is that when you want to add something into an object that you might not own, and you don't want to collide with any other keys, creating a new symbol and adding a property to that object using a symbol is the safest way to do that. The other use case for symbols is that if you do not publish your symbol value that you use to add something to an object; no one else will know how to get to that piece that you added to an object. So if I create a new symbol, and let's assign it to a variable s_name, and I store the name of an employee using s_name as a key instead of effectively the string_name, then no one will be able to know how to get to this particular property. S_name is not exported, and I'm about to save this file, notice we'll go from Joy is working back to Scott is working, because now, back in default.html, even though someone is fiddling with _name, that's not internally what the object uses to store the name anymore, and what the object stores is not available from that module, and you can think of this combination of symbols and modules as a very lightweight information hiding technique. And modules in general are a great way to hide things, because if I do not export something, it's not available for use outside of the module.
Modules All the Way Down
One question you might have had so far is can I use a module from another file that is a module? And the answer is yes. Let's see how easy that it is. In addition to employee.js, I have a brand new file company.js. What I want to do from here is export a company class, and let's give every company two abilities. The first ability will be the ability to hire employees. I will take the names of the employees as a rest parameter, and then I could create an employees array by saying names.map for every name that you give me, turn that into a new employee by that name, so using the new arrow function syntax. This will require me to import Employee from ./employee. That's the path to the module I need. And we could also give every company the ability to tell every worker to work. And one fancy easy way of doing that and combining all the results together is to use an array comprehension. For e of this.employees, what I want to produce is the value returned from invoking doWork on each employee. Let me save that file. I also have a main.js. This is what I would consider to be the driver of the program. Like the main entry point, I will import that new company class from company.js, and then I want to create an instance of a company. I want to tell that company to hire some employees. Let's hire Joy, Sue, Tim, and Tom. And what I'll do is just a document.write on company.doWork. Company.hire should use array.map to turn each string into an employee object, and company.doWork should use that array comprehension to turn every employee into the result of invoking e.doWork. And now what I want to do is save main and change my default.html to use this code instead of what we're currently doing. Now notice main.js has no exports. It is a module, but it doesn't publicly expose anything. Really the only reason to load this module is to introduce the side effects that it has, which is to instantiate a company and do a document.write. All I need to say to produce that side effect is to import that module and have it execute. I don't need to import anything specific from that module, I just need to import /es6/main. The module loader will load that module, it will execute, and we will get the output that we're looking for, even if it's not that pretty. So what's happening behind the scenes? Well, one day there will be a module loader in the browser, and when I have a statement like import ./es6/main, it's going to go out and fetch main.js. Main.js is going to import something from the company module, so the module loader will have to go out and fetch company.js, and company.js references employees, so that will require the module loader to go out and get employee.js. This sounds like a lot of work, and it's something that we typically try to avoid in browser-based applications, and there is help on the way. Someday we expect to have smart module loaders natively in the browser, someday we expect to be using HTTP 2, which allows for multiple streams over a single connection and the ability to prioritize resources, script files should be able to load much faster, but until we reach that day, what are some of the techniques that we can use to take advantage of ECMAScript 6 modules without waiting for standards and for browsers to catch up? Let's take a look at one strategy next.
ES6 to AMD
Currently the way I'm running my scripts is I am using traceur.js and telling the traceur.WebPageTranscoder to run. The WebPageTranscoder dynamically compiles scripts that have a type of module, so it will be going out and loading up these scripts, transpiling the contents into something that will run with ECMAScript 5, and providing all the shims and polyfills so that everything just works. But what traceur has to do is go out and individually load these files. We could see that if we open up the developer tools and look at the network panel when I request this page, you can see traceur going out and grabbing employee.js last. It pulled in main, it pulled in company.js, it's basically walking the dependency tree that we are creating with our import statements. And one of the things we'd have to be careful of in a real web application is having too many HTTP requests for script files when we should be trying to minimize the number of requests. So one approach that will be useful in solving that is using traceur to precompile some of these files. And if I npm install traceur, that will put a copy of traceur into my node_modules directory here. I could also install it globally. But now I could go into node_modules in the bin folder, there should be a traceur command that will allow me to compile files, and obviously you would want to have this set up as part of a build process. If you're a node developer, you'll probably have a grunt file or a gulp file to do this automatically, but let me show you what it would look like to create an ECMAScript 5 compatible file called employee.js using the ECMAScript 6 version of employee.js that I have in this application. I'll need to turn on the experimental flag that's currently required to use block scoping with the let keyword with traceur, and then the important flag for this module is the modules flag. What I'm doing is telling traceur to take my source file, which will be app\es6\employee.js, and transpile it into ECMAScript 5 and create an AMD compatible module. And it just created that file, I can open it up here in Sublime text, and you can see the define statement. Define is one of the AMD APIs, so now I can use a mature, stable, efficient AMD module loader like require.js to manage my scripts. Now the great thing about require.js is I could compile all of my ECMAScript 6 files into individual files that are AMD compatible, and then when a tool provided by require.js is the require.js optimizer, it can combine related scripts together and minify those files so that everything is optimized to load in a browser. I still get to develop with individual files that are ECMAScript 6 modules, but until we reach that time where browsers are really working well with ECMAScript 6 modules, we're probably going to be using an approach similar to this so that our applications can work efficiently.
Summary
In this module, we looked at exporting and importing with ECMAScript modules. A module system is something JavaScript has been sorely lacking, and it's become more evident these days as we are building more complex apps and writing more JavaScript code. Modules will give us safer code that's more isolated and easier to develop and test. There's various standards and APIs that have popped up over the years to deal with this issue. We talked about these, CommonJS and AMD, and chances are, we'll be using these standards for a little while longer as the browsers and other platforms adopt the new official ECMAScript standard and mature.
Using ES6 Today
Introduction
Hello, I'm Joe Eames. Even though the ECMAScript 6 standard is pretty much set in stone at this point, it will be used before the majority of users are on browsers which implement ES6. So as developers, we have two choices. We can wait, not taking advantage of all the great features that ES6 is bringing, or we can start using it today, and start benefitting in our coding. In this module, we'll be looking at different ways to use ECMAScript 6 today. We have essentially three options for using ES6 today. The first is to rely on the execution environment support. That means either the browser or Node. The second is to use a transpiler. A transpiler is a tool that will convert one language into another. In our case, we are converting from ES6 to ES5. Tracer is a transpiler, and we will talk about others in this module. And the third is to use polyfills or shims. Polyfills and shims are a way to add new functionality to an existing environment. For example, the map object is new in ES6, but it could easily be created in ES5 by just creating your own map class. You can use either polyfill or shim to describe this. The difference between these two terms is fairly academic, and I will use them interchangeably throughout this module. Of course, we can use a combination of these three methods, depending on our needs. We'll cover each of them in this module. Before you consider using ES6 now to be impossible because of browser support, you should know that using ES6 in production products is becoming more and more popular. In fact, both Angular 2.0 and Ember are currently being developed in ES6. My coauthor, Scott Allen, has even been working on a production application for awhile using ES6. So in this module, we'll look at the different tools available to let you use ES6, not only to learn it for the future, but to actually take advantage of it today. I won't be giving you a comprehensive list of the tools available since new ones are popping up all the time, but there's a great GitHub repo that Addy Osmani has created that keeps a pretty exhaustive list of the tools that could be used in one capacity or another with ES6. That page is right here at this URL. I highly recommend going there and spending some time looking at the different tools listed. Most of all, I suggest you consider introducing some ES6 features into your current product, whatever it may be. Most of the tools I will cover in this module are production-ready and have already been used in many applications.
Browsers
The obvious first place where we can go to run ES6 today is in the browsers themselves. All of the major browser manufacturers are currently very busy implementing ES6 features into their browser engines. Already a lot of functionality is supported by browsers, and more and more will be supported as time goes by. Before we get any further, let's review what we have to do to run ES6 in today's browsers. Again, it's important to remember that as time goes by, browsers are going to sport more and more features without requiring you to do anything. As of the time of this recording, few features of ES6 are supported in the release version of the major browsers. For Chrome, we have to enable the experimental JavaScript flag. We do this by going to chrome://flags, and then we go down and find the experimental JavaScript flag, and then we click Enable. Doing this will require us to relaunch Chrome, and clicking this relaunch button down at the bottom will relaunch Chrome with the experimental JavaScript flag enabled. The release version of Firefox already supports a few ES6 features, but downloading and installing Nightly Firefox will enable a lot more features. That's easily done by Googling Firefox nightly. This will quickly lead you to the download for Firefox Nightly. Also, the beta version of Firefox is viable as well. It's a lot more stable than Nightly, and still supports a lot of ES6. It's helpful to know that after installing Firefox and Firefox Nightly, you can't run both of these at the same time. So if you want to switch from one to the other, you'll have to close down Firefox completely and launch the other version. Currently Internet Explorer and Safari only support a few pieces of ES6, but look for that to change. Browsers are completely suitable for playing around with ES6, but for a production application, they are extremely limiting, because you will have to control the browser that your users are running in order to assure support for the features that you're using. This makes it only suitable for internet and extranet type applications where you actually can dictate which browsers your users are using, but again, over time this will change. At some point in the future, relying on the majority of your users to be using a browser that supports a lot of ES6 features will be a viable choice for a public website, but just like deciding on browser support today, it's a decision you'll want to make carefully, and use some analytics and usage statistics to help you guide that decision.
Experimenting With Traceur
The most common way that people today are using ES6 is with traceur. Traceur is a project ran by Google, which takes ES6 code and compiles or transpiles it to ES5 code. The primary purpose of traceur was to enable developers to begin using ES6 and let them give feedback to the TC39 group who is responsible for defining the ES6 standard. Because of the way that traceur works, and its goals, it doesn't necessarily produce the most efficient code. This doesn't mean that it's slow, but if speed is your primary purpose, then using ES6 and transpiling to ES5 with traceur may not be your best course of action. Traceur also doesn't support all of ES6. The reason for this is that not everything in ES6 can be effectively implemented without major changes in the runtime execution engine, but look for this to improve over time. Traceur is an engine which can be invoked and used in many ways. In this section, we'll look at a few of the ways to use it mainly for experimentation. The first and simplest of these is to just use the hosted traceur REPL. REPL stands for read-eval-print-loop, and it essentially means an interactive shell. Traceur has an online REPL at this URL here. To use the traceur online REPL is very simple. You just type in your code over here on the left, and the online REPL will turn it into ES5 code over here, and it will also execute your code. So, for example, if I were to type in class myClass, like that, then you can see that it's transpiled that code into the equivalent in ES5. If in here I type in console.log, then you can see that it's been logged out to the console. Over here in the upper right is a list of options. If you click this button, a dropdown list will appear with a whole bunch of options that you can check. It's often best to come in here and just turn all of the options on. That way you'll get the most compatibility with ES6. For example, if you were to put in a let statement, you can see that I'm getting an error, unexpected token let. But if I come into here and add in blockBinding, then all of a sudden the code compiles. So often it's best to just turn all those flags on when playing around with ES6. Another nice feature of this online REPL is that it lets you play around with the different options in traceur and let you see what they do. The next place that you can use to experiment with ES6 is with Plunker. You've seen this at the beginning of this course, but I'll go over it briefly again here. Inside Plunker, after creating a new plunk, go into the search packages and type in traceur. At this point, we'll click here to add traceur to our plunk, and you can see that these two scripts point at two traceur files. They are traceur.js and bootstrap.js. We'll talk about the bootstrap.js file more in a second, but this is the traceur engine, and the bootstrap.js file is a file that will launch the tracer engine and run it in the browser. Now that I've added these two files, I can write ES6 code right here inside of my project. I can do it directly in the HTML file by adding a new script tag and giving it a type of module. This will let traceur know that this is some script that's written in ES6 that needs to be compiled to ES5. We can also set options on traceur that correspond to those same options we saw on the traceur REPL. We do this in another script tag, which does not have a type of module. To set options, there's a global variable called traceur that has an options object, and from here are all the different flags that we can set. So I can turn on the experimental flag or any of the other flags that I want. If I want to write ES6 code inside of an external file, like this script.js file, then all I need to do is go to this script tag here, and add in the type=module, and now that will be transpiled by traceur into ES5. The last place that we can experiment with traceur is inside of our own projects by simply including the traceur engine and the traceur Bootstrap file. Here I've created a quick little project with a default.html file that includes those two traceur files. You can see them here, and here. I've also turned on a couple of options. I've turned on the experimental flag, and I've turned on sourceMaps so that traceur will produce sourceMaps for the transpiled code. And you can see that I've put a script tag here in the page of type module, which lets me write ES6 code inside my page, and I've also got a script tag of type module with a source attribute, which lets me bring in external code, which traceur will transpile down to ES5. So you can see that getting traceur to transpile your code is pretty easy. It just involves these two script files. And one of the things you may be wondering is this line of code here at line 12, which is commented out. This line of code is the only code that's inside the Bootstrap.js file, so instead of including Bootstrap.js file, you can just include this line of code. And one of the drawbacks of using traceur this way is that all of your ES6 code has to be transpiled on the fly in the browser. That's going to be a performance cost that you won't necessarily have to pay. That's why this is a better method for experimenting with traceur than actually using it in production. In the next section, we'll look at some effective ways to use traceur in production.
Using Traceur in Production
In this section, we're going to look at three specific ways that we can use traceur in production. Those three methods are, the command line, a third-party tool, such as Grunt or Gulp, and lastly, using traceur in WebStorm. Ultimately, each of these ways simply uses the command line version of tracer, but which method is best for you and your uses can vary from case to case. We'll start with the most basic method, which is using traceur from the command line. This is easily scriptable into some kind of a shell file or incorporated into a build script. The first thing we'll need to do is install traceur globally using npm. This command will install traceur globally, and once that's done, we can then execute traceur from the command line. This is done using the traceur command and then passing in the parameters that it needs in order to execute. If you ever need any help with traceur, you can always do -h, and that will show you the help for traceur. Probably the most basic execution of traceur is to start with the command, then add in any option flags that you need, for example, I'm going to add the block-binding flag, and I'm going to set that to true, then you use the --dir flag, which tells traceur to convert everything in the input directory into the output directory. After the --dir flag, you give it the input directory. In this case, we'll assume I have a source directory I want to transpile from, and then the destination directory. This command will transpile all JavaScript files inside the source directory into a file in the same name in the out directory. Now, if you want to transpile just specific files, then you can use the --out parameter, and then list all the input files one by one. Let's assume that in my source directory, I have a users file, and I also have a widgets file. This command will transpile the users file and the widgets file, take the result, append it together, and put it inside the build file in the out directory. So the syntax for traceur is simple and straightforward, but it gives you a lot of options. The next method that we'll use at is using Grunt or Gulp to automate traceur. Here I've created a Gulp file to transpile files using traceur. The first thing I've done is require in Gulp and traceur. Next, I've created a Gulp task named traceur, which reads in all the .js files inside of my ES6 directory, pipes them into traceur where I can set any traceur flags, in this case, I've set experimental and blockBinding, and finally pipes them all out into the ES5 directory. This works essentially the same as using the --dir flag when running traceur from the command line. And then finally, I've made traceur the default task for Gulp, so that if I type in gulp from the command line, it will run this task. Now, of course, you'll have to install Gulp and traceur in order for this to work, which we'll go back to the command line and simply type in npm install gulp and gulp-traceur. This will install both of these modules. Now you can either install them globally using the -g flag, or if you want to install them locally, you'll still need to install Gulp itself globally so that you can run Gulp from the command line. At that point, you're ready to use Gulp to automate your workflow. Of course, with Gulp you can do lots more than just run a single task, we could set up a watch task to run traceur anytime any of our source files changes, and lots of other things as well. Gulp is a very full-featured automation tool. The other tool we can use is Grunt. Grunt is just like gulp, it's an automation tool. It just uses a different format for its configuration file. Here I've configured Grunt to run traceur, I've set the experimental option to true, and then I've configured it to transpile all the files in the ES6 directory into the ES5 directory, and of course, don't forget to load your task using the loadNpmTasks function. And finally, I've told Grunt that traceur is my default task, so that if I just execute the Grunt command, then traceur will run. And just like Gulp, Grunt can be configured to watch all of our source files and transpile them whenever any of them changes. Like Gulp, Grunt is an npm module, and we install it on the command line using npm install grunt, and we've also got to install the grunt-traceur module. Grunt's a little bit different than Gulp. Once those two modules have been installed, we also need to install the grunt-cli module, but that needs to be installed globally. We do that by running npm install grunt-cli with the -g flag. The final method that we'll look at for using traceur in production is WebStorm. Of the three methods, WebStorm is the least effective, because this could not be incorporated into a build process. But if you're developing solo, this is a very effective way to transpile your code to ES5, and it alleviates the need for you to spend time setting up Gulp or Grunt or something else to run traceur on the command line. In order to transpile our ES6 code using WebStorm, we first have to go to the settings dialog. On Windows that's Ctrl+Alt+S, on a Mac that is Command,. Once inside here, go to the JavaScript node right here, and then change the JavaScript language version from ECMAScript 5.1 to ECMAScript 6. Once I hit OK on this, I'm going to be prompted whenever I open up a JavaScript file. And you can see this prompt at the top asks me to add a watcher to transpile ECMAScript 6 code to ECMAScript 5. I'm going to click that Add watcher button, and the new watcher dialog box appears. Inside of here, I need to first make sure that the path to the traceur command file is correct. Usually the default is correct. Then, I can add any additional arguments using the arguments box. This is useful if I need to set any additional command line flags. For example, if I want to set block-binding on, I can add that flag here using --block-binding, just as if I was on the command line, and then setting that to true. Once I've got this set the way I want it, I can click OK, and now WebStorm will automatically transpile my JS files into ES5. And then the last thing I need to set is where the output path is. This will determine where the ES5 versions of the files will appear. In this case, it's set to create an out directory at the root of my project and put every file inside of that directory with the same path that the original source file had. Of course, I could change that to whatever I want, such as changing from out to build. Once I hit OK, now every time I make changes to one of my JavaScript files, it's going to run traceur and transpile my file into ES5. So there we've seen three different methods for using traceur in production. Each of them has their strengths and weaknesses. By far the most common way that people use traceur today is using either Grunt or Gulp, but the other two methods can be effective based on your scenario.
Polyfills
In this section, we're going to look at how to use polyfills to begin using ES6 today. Of all the methods that we'll look at for using ES6 today, this is perhaps the least disruptive, since it just involves including a library in your project, and doesn't require any additional steps. Polyfills, as mentioned previously, are libraries that give new capabilities to old browsers. As a rule, they're non-disruptive. So if the browser already implements the functionality, then the polyfill won't run and you can use the native implementation. Another thing that's important to understand about polyfills is that not all features in ES6 can be implemented through polyfills. Some features, those that involve new syntax, require a transpiler, but a lot of features can be implemented by polyfills. Now rather than give you a list of polyfills for each of the features in ES6, I'm just going to show you how to find polyfills and how to implement them. There are many different ways to discovery polyfills. You might stumble upon them in a blog article, but the most common way, of course, is just to find them through Google. So let's do something simple and search for a polyfill for the map object. I'm just going to type in es6 map polyfill. And you can see, I've got a list of results, each of which is a polyfill for the ES6 map object. Some of them are polyfills for more than just map, some of them are polyfills for just map itself. Doing this, I can go through each of these results, and pick the polyfill that works best for my situation. Implementing a polyfill is no different than implementing any other third-party library. For example, let's say that I wanted to implement a polyfill for array.from and array.of. Well, I happen to know that Rick Waldron has authored a nice polyfill for those two functions, and that's at this URL here. So going to this URL, if I want to use this polyfill, I'll have to download it. This polyfill is a node module, but not a Bower module. So if I want to implement a node, it's as simple as using npm install es6-array-extras, but if I want to use it in the browser, I'll have to download the file manually. So going into lib, going into the js file, selecting the Raw version, so I'll save that off into my Demos directory, into my polyfills directory. And now that I've got the polyfill downloaded, I can implement it. So I can now go into my web page and add that script file, and here inside my body, I'll create a new script tag, and I can now use those two new array functions. So if I wanted to create a new array with a single element of 4 in it, that would create for me an array with a single element of the value 4. And there's an example of using polyfills so that you can have ES6 functionality in today's browsers.
ESNext
In this section, we're going to look at ESNext. ESNext is very similar to traceur in that it's a transpiler that allows you to write your code in ES6, and it will get transpiled down to ES5. There are a couple differences between ESNext and traceur. ESNext supports a smaller set of the new syntax in ECMAScript 6, but ESNext doesn't require a runtime library to be included in your code. Just like traceur, ESNext is a node module, which we install using npm. Since compiling with ESNext means using a command line utility, you want to install it globally with the -g flag. Once that's installed, using ESNext is rather simple. Just like traceur, you call ESNext, tell it which files you want for the input and which files you want for output. Now if you just want to test out ESNext, you can compile a file directly to the standard out by calling ESNext, then passing in the name of a file. Executing this will print out the resulting ES5 code to the console. Of course, you'll want to use ESNext to compile your ES6 files into ES5 files. Doing that is straightforward. We'll call ESNext, add the -o flag, which lets us specify an output directory, I'll specify the output directory of build, and then we pass in the files that we want to compile. In this case, I'm going to compile all those .js files inside of my lib directory. After executing that, all the files in my lib directory have been compiled into the build directory in ES5. Like traceur, ESNext also has Gulp and Grunt tasks that allow you to automate ESNext in your workflow. As I mentioned, ESNext supports less features than traceur does, and it's far less popular, but ESNext can be an alternative, because the code that it produces is quite a bit different than what's produced by traceur, and those who use ESNext often prefer the code that it produces over the code that traceur produces. So whether ESNext or traceur is right for you will really depend on your situation, but it's definitely worth taking a look at.
6 to 5
In this section we're going to be talking about Babel. Now, if like me, you grew up pronouncing the Tower of Babel, with the word Babel and not Babel, it's going to seem a little strange to you to look at this word and pronounce it as Babel. But indeed, the official pronunciation confirmed by the creator, is Babel. Babel is quickly becoming the defacto transpiler for ES6. In fact, it's currently probably the most popular transpiler used by front end developers. Babel is extremely versatile, and not only transpiles ES6 to ES5, but also ES7 and even things that aren't even a part of the ECMA script spec that are becoming very popular. Babel has all the features of traceur. Let's look at an example of using Babel and some of the output. The first thing we need to know is how to install it. The default way to install it is globally using npm. With this command, although this is only useful if you want to be able to run it from the command line. It's not very popular to run it from the command line, most people use it as a Grunt task or a Gulp task or as part of webpack. We'll just use the command line for this demo, but you do need to know that there are those other options out there, for however you want to use it. Now, I'm not going to install it globally because I already have. So in order to use Babel from the command line, you simply type in the Babel command, and then ________ file, and we'll transpile this. And that puts it out to the console, and you can see there the result of the Babel transpiler. Now of course, that's only useful if you want to look at what Babel produces. Normally we want to put the results into a file. So, in order to do that, we just give it an outfile parameter, and that will transpile the contents of unicorn into the outfile. If I want to add source maps, I simply add the --source-maps flag, and that will produce the source map alongside of the resulting transpile file. And of course just transpiling one file at a time is kind of tedious, oftentimes we want to transpile an entire directory, so let's transpile our lib directory. And instead of an outfile parameter, we'll give it an --out-dir parameter, and we'll send the contents out to the build directory. And you can see it's processed both files, and outputted them to the build directory. And finally, we can get Babel to concatenate for us, and to do that we just do Babel the source files or source directories. And then instead of an --out-dir we'd use an --out-file. And there you have the basics of using Babel on the command line.
ES6-shim
In this section, we're going to look at ES6 Shim. ES6 Shim is a lot different from traceur and ESNext, because rather than being a transpiler, it's simply a collection of shims or polyfills. That means that ES6 Shim is extremely easy to implement in your project, since it doesn't require messing with your workflow or your build. It's simply a single JavaScript library that you could include. If you're on Node, you'll install it with npm. If you're using it for the browser, you can install it with Bower. Let's go ahead and install it now. Now that it's installed, I can include it in an HTML file. All I have to do is add a script tag that points to the ES6 Shim JavaScript file, and now I can use the shims available in ES6. Let's take a quick look at some of the features that are provided by ES6 Shim. Here on the readme for the GitHub repo, they list the shims that are provided by ES6 Shim. So you can see that they provide map and set, promise, the new functions on strings, number, array, array prototype and object, as well as math. Of course, this list could change as time goes by, but you can see that it's a fairly comprehensive list of polyfills. So going back to my project, once I've got that file included, I can begin to use the features. For example, I can use Array.of to create a new array, I can create a Map, and I could also create a Promise. All these features are now available to me just by including this one JavaScript file. And the size of the file is very manageable. The unminified file is 63 KB, but it also includes a minified version, which is only 25 K. So ES6 Shim is one of the easiest and simplest ways you could begin using ES6 features today.
ES6 in Node
In this last section, we're going to look at how to use ECMAScript 6 with Node. Now, of course, Node is just like any other JavaScript execution environment, such as the browser, so you can always use a transpiler like traceur or ESNext in order to use ECMAScript 6 in Node, or you can use polyfills or ES6 Shim, but Node does support a number of features of ECMAScript 6, which you can enable if you're running Node version 0.11 or later. At the time of this recording, the latest stable version of Node, 0.10, does not support any ES6 features, so you must be using the unstable version. Because of this, it's a great idea to install a Node Version Manager, such as NVM or N if you're on Linux or OS6, or nodist if you're on Windows. Downloading and installing one of those managers is beyond the scope of this course, so I'll only mention it as advice. But you can find those managers at these URLs here. Again, nodist is for Windows, NVM and N are for Linux and OS6. I want a Windows box, so I'm using nodist, so I set my nodist to use the unstable version of node. Now inside of this directory, I've created a single file that has some ECMAScript 6 code inside of it. You can see I'm using a let statement, and I'm also using a map. Now let's go see what it takes to execute this file in Node. Back at our command line, we would normally type in node and then creatures.js. If I execute this, I'm going to receive an error, because those ES6 features are not enabled by default inside of Node, even in the unstable version. So I've got to go in and add in two flags. I've got to add the --harmony flag, and I've got to add the --use strict flag. With those two flags enabled, I can now execute code that has ES6 features. And you can see that the code executed with no errors. Of course, Node is only supporting a small subset of ES6, but as time goes by, it will support more and more features. You can always check the compatibility list to see what Node is currently supporting. There is even a major web framework called Koa that uses ES6 features and so it has to run with the harmony flag on.
Summary
In this module, we looked at several different tools that you can use to take advantage of ES6 today. We looked at using browsers as they are today, then we looked in-depth at traceur, one of the more popular transpilers, both for experimentation and for production use. We also looked at using polyfills. We took a look at three different projects, ESNext, Babel, and ES6-shim. Finally, we looked at how to use ES6 with Node using one of the above methods or by turning on the harmony flag. All of these tools together create many different ways to take advantage of ES6, and for the vast majority of developers, one of these methods will be feasible to implement today.
Course Summary
ECMAScript 6 is an exciting new technology and brings a lot of great features to JavaScript. We are excited for what the future is bringing to the web, and other types of JavaScript development. On behalf of myself and Scott, we want to thank you for watching this course. We enjoyed putting it together, and hope that you will take what you have learned and apply it in your every day development.
Course authors
Joe Eames
Scott Allen
Course info
LevelIntermediate
Rating
(609)
My rating
Duration4h 51m
Released17 Sep 2014
Share course