What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
ES6: The Right Parts
by Kyle Simpson
Learn new ES6 JavaScript language features like arrow functions, destructuring, generators, and more to write cleaner and more readable programs.
Start CourseBookmarkAdd to Channel
Table of contents
Description
Transcript
Exercise files
Discussion
Recommended
Arrow Functions
Introduction
This is my preferred place to teach JavaScript, so it's such an honor to keep being part of it. We just did a quick count, this will be my ninth course taught for Front End Masters, so that's pretty exciting. And I always tell this, it's actually the first place I ever taught. Mark gave me my start as a teacher, called me up out of the blue and said, I think you should come teach, and I've made a career out of it since then, so it's an honor to come back to that place and to talk to you about, I think, maybe one of the most important topics that I've taught on, which is really kind of beckoning towards the future of where the JavaScript language is headed. And that future, I believe, is very bright. It's a really good future. There was a long period of what we would call the dark ages for this language and some of you, like me, were around to experience that, to experience all the problems of feeling like the language was just dead, it was dead in the water, it wasn't going anywhere. And now we are in this period or rapid growth, expansion of the language, and there are a ton of things that have been added over the last year or two. If you've kind of been taking a nap for the last year or two and you wake up, it's not the same JavaScript anymore. You can't read a blog post these days without seeing an arrow function somewhere or some new feature that somebody wants, like the double colon bind operator, which isn't even in the spec yet, but we already write code assuming that that thing is coming, right. So the language is rapidly evolving and those of us who spend time writing and teaching about JavaScript have to grapple with, what does that mean, you know, if I write something and then 6 months later that thing is already obsolete? So it's a fun and exciting time for the language. There are a lot of amazing things added, and then there are some things that maybe around so great. And there's a lot of subjectivity, of course everybody has their own opinions. Today's class, ES6: The Right Parts, is designed to give you an opinionated guide through the things, not just am I saying these are the correct parts, but I really mean, these are the right parts for you to focus on first, that's what I really mean by the title. What I really mean is this is the right stuff that you should be paying attention to first. And I'm going to give you my own personal take, my own narrative on why I think ES6 is important. It's really all about using the tools that are now in the language to effect a goal that I think we should have had for a long time, but most of us didn't really adopt for, and maybe haven't even fully yet adopted. But it's become such a deeply held belief of mine as an educator in JavaScript that you could almost call it a religious belief. That code is not about instructing the computer, code is about communicating with other human beings. We are really good at instructing the computer on what to do. Many of you are expert developers, but how good are you at communicating to other human beings? Did you know that researchers say that within the span of time that you spend maintaining your code, and let's be honest, most of us our jobs are more primarily maintaining existing code, not opening up a brand new blank file and writing brand new code for the first time all day every single day. Maybe you're lucky enough to have that job, but most of us spend our time developing on code that already exists. Either code that we wrote yesterday or last week or last month or something that somebody else wrote on our team. Maybe even somebody that wrote it years ago and we're maintaining legacy code, as they say. Alright so we spend most of our time maintaining code, and researchers say in the time that you spend maintaining your code, which is the majority of your day, did you know that you spend 70% of your time just reading to understand the code? No wonder the global average for lines of code written by a programmer is under 5 lines of code per day. Because we spend 7 hours and 49 minutes a day reading the damn thing before we can figure out what 5 lines to write and where to put them. Because we really, if we're honest about it, we kind of prioritize authorship of code. Don't we prioritize how quick and easy and fast it is for us to write our code and maybe not so much pay attention to what it is to read the code? And I think many times people conflate these two. If I can write the code easily, that means the code is easily read. The fewer characters I have to write, the fewer characters somebody has to read, these seem to go logically together and yet actually they fly directly in competition with each other. That same set of study and research that I'm talking about suggests that the notion of readability is not quite so subjective as we might assume. Most people probably assume that readability is all about your own personal preference and there's no way to possibly make a statement about whether something is readable or not. And these researchers set about to figure out if there was a way to have an automated metric for this code readability thing and they discovered that the most important factor that leads to somebody judging a piece of code is readable is familiarity. Have you seen that code before or have you seen that pattern of code before? If you have, regardless of how many characters it includes, you call it readable, and if you haven't, regardless of how many characters, you call it not readable. It's just simply, are we familiar with it? Now we oftentimes just assume everybody on the team is going to know everything and we're going to be super hyper-optimized, we're a racecar that's going around the track at 100 miles an hour. I worked at this startup one time and the CEO of the startup used the stupid metaphor like, we're a racecar and we have to change the tires without a pit stop, right? That's the dumbest, stupid startup metaphor ever, right? But that's kind of how we think about like writing our code. We're all like this finely tuned machine and we don't have to worry about bringing people up to speed, expect don't we? Isn't there always going to be a new person joining the team? And isn't it always the case when a new person joins the team, you just sort of say, hey first 2 weeks, week or 2, just go read the code to figure out what it's doing, right? Anybody in here, like me, I had a lot of jobs and I was always told just go read the code to understand it. Do you know how much I actually understood from reading the code? Very, very little. Because I think that team probably prioritized writing the code efficiently as a team, as a well tuned machine, and didn't always necessarily think about what's it' going to be like if somebody reads this and isn't already familiar? Is there a moderator question? Yeah? Are there any links to this research? Yeah, I will throw out some links to the research, specifically there's a white paper that I've read about automated code readability. If you Google search automated code readability, you'll probably find it. But that white paper sites some other papers on this research. So, I think it's really important that we maybe put reader of the code as a more important priority than writer of the code. So with that as our backdrop, we turn our attention to ES6. And most people think that ES6 is all about a whole bunch of brand new shiny toys that make us have more fun writing our code, and I'm going to tell you that it's not so much about writing your code, but it's really more about making that code more readable. Specifically, there is a notion in programming, and maybe you've heard this before, but there's a distinction that we make between styles of code, and typically we can put code into somewhere in a spectrum, but into two camps, imperative versus declarative. Some of you may have seen frameworks out there that kind of sway heavily one way or the other, for example, Angular, you know, it's very declarative, we put all of the stuff in the markup. And there are other frameworks, which I think would be more on the imperative scale, like for example in Ember or something like that. So we have this spectrum of imperative versus declarative. Here's how to put it simply. Imperative means telling the computer how to do something. Declarative means telling the computer what the end result should be, let the computer figure out how to do it best. Okay. Now it is not the case that all declarative code or all imperative code is correct. That is not the case that that will be the best code is if you swing that pendulum one way either way. It is, however, the case that many times declarative code, used appropriately, used measured, used with caution, declarative coding style does tend to be more readable to those who are not already familiar with the program. If I threw up an algorithm here, and I had a bunch of for loop and if statements and stuff, you would have to read every single line of that and trace through, essentially run the program in your head, to understand what it's doing. But if I used a declarative form that just simple said, here's the end result, I want all these numbers squared minus 1, right, I don't want you to have to go through and figure out what that is, I want to just tell you that's what this code does. And a declarative form, a syntax that is a declarative form says, the end result should be numbers squared minus 1, and allows, as much if possible, allows that to be taken care of as an implementation detail. So ES6, the narrative that I want to leave you or I want to start today, not leave you, I want to start today with is that ES6 is really about adding more declarative forms so that we can take the idioms, all the stuff that we've been doing in our existing code, and express them in a way that will be, once somebody learns ES6, much more familiar because a person will not have to spend so much time reading through each of the details of the logic, they'll be able to recognize at a glance the declarative form and know what that declarative form is doing. The goal being that we make code that's more readable for those who aren't already familiar with our code. So wherever possible today I'm actually going to give you the before and the after comparison. I'm going to try to start with what we've been doing and then talk about what we can now do, not new functionality per se, virtually nothing in ES6 is something you couldn't do at all before, there's a few little things, but most of it is just a better way to communicate what we've always been doing. Okay. And I also want to point out this, don't miss this point, there's an awful lot of narrative around ES6 that says blank is the new blank, arrow is the new function, let is the new var. We've rushed to this idea that all the new stuff means all the old stuff sucks and it's legacy and it's bloat if you're using all the old stuff. And you'll actually find very little of that from me today. I don't think that there are very many examples in the history of programming where that has held globally true, blank is the new blank. This is an additive set of tools that gives us more ability to communicate what we want. But all the existing stuff stays valid, we preserve backwards compatibility. As a matter of fact, there are going to be things, and I'm going to disagree with a lot of my peers here, but there are going to be things that we've been doing that we're going to continue to keep doing. We should keep using certain features, like for example, shockingly I'm going to tell you should keep using functions, and I'm going to tell you you should keep using the var keyword. We'll go through all of this stuff, just because we have the new stuff and there are places where that will be more communicated, doesn't mean that the old stuff needs to necessarily be thrown out the window. Okay, so look at ES6 as a way to improve the readability of your code, not throw away all the old stuff that we're already known.
The Arrow Function
I'm going to take you through a tour of the stuff that I think you should pay attention to, but by popular demand I have to start by going into a feature that I really don't think you should pay attention to. I wouldn't really call this a right part in the sense of this is the place to start. It's not objectively all bad, there's not zero value to it, but it's the place where everybody's journey with ES6 seems to start, I'd be remiss if I didn't start your journey with the arrow function. Now I want to talk about the arrow function and I'm going to give you my opinions because this is all about an opinionated guide. I want you to make your own decisions, but I want you to make those decisions in an informed way. That really undergirds everything that I do as a teacher. I want you to make those decisions in an informed way and I think a lot of times people sort of just follow whatever the hype bandwagon is. If every blog post out there is using an arrow function we assume we're all supposed to use arrow functions. Okay. So I want to talk to you about what the arrow function is, some of the problems with it, some of the reasons why it might actually be a valid thing, why it exists and where it might be useful, and we're actually going to get an exercise to practice with it. And I'm going to go ahead and predict that most, if not all, of you are probably not going to be able to complete it because it turns out arrow functions are actually a bit more complex than we've been admitting to ourselves. I accidentally created a scenario where myself, I failed it the first few times I wrote it. So we'll go into that and we'll see the pros and cons and the places where an arrow function ought to be useful. Okay, so what are we talking about? We're talking about this symbol specifically, the fat arrow, is supposed to replace that old and busted function keyword. Right, because function, what is that 8 characters, that's just so many characters to type. Let's type 2 characters that in the US keyboard require also the Shift key to express, so actually how many key strokes are we really saving? But we don't want to write the word function. We also don't want to write the word foo because who wants to name their functions? Naming sucks. And we also don't want to write the word return because that sucks. We just want to write nice simple arrow. Okay. So the equivalent is to have this arrow function and it's an expression, so it can't stand alone like a function declaration, it's got to get assigned to something, so if we had a variable called foo, we could assign it. Now this open close parenthesis here, that's the parameter list, and when there's one and only one parameter, like for example if it was x then you get to just put that there, without parentheses. Ooh great, we get to get rid of our parentheses. You'll notice I didn't have to put any curly braces, I didn't have to put a return keyword, and if you're really super hipster, you could have even left off the semicolon, gone semicolonless, man you're just saving characters all over the place, right. So this is what everybody gets excited about with ES6 is oh we get these nice short concise syntactic forms. So is the hype well deserved? Now I want to talk to you about some variations on the arrow function syntax, but before I even go into the details about the variations on it, the fact that there are variations is kind of the problem. Because there is virtually no variation in syntax for the standard function. The standard function shows up with the function keyword, a name, some parentheses set, a curly brace, generally a return keyword, almost no variation whatsoever syntactically speaking. Meaning, everywhere that you use that form, there is already a built-in familiarity. Everybody that reads your code looks at a function and knows exactly what a function does, knows where the curly brace scoping block starts and ends. You start introducing a new form and you do need to ask yourself when you're trying to make an apples to apples comparison, you need to include that question of is somebody going to be able to read and at first glance still be able to understand this? And also, by the way, ask your developers, ask your developer self, am I going to be able to write that? I saw a tweet recently from one of the most vocal proponents of the arrow function, he was an early adopter, he's been writing all of his blog posts using the arrow function, and he tweeted out, this was a few months ago, he tweeted out and he said, you know, I love arrow functions, but my instinct, I can still write the word function faster than I can write the arrow symbol. After years of writing the arrow function, he still soft of sways back to it's a little bit easier to just quickly write the function keyword and so he writes that and then he goes back and refactors it to the arrow or something like that. So maybe it's not quite so objectively like amazingly more readable, maybe there are some caveats to the readability of the function. The fact that there's a lot of variance in syntax means that the reader of your code, having not already been super familiar with this, and by the way there just aren't that many JavaScript developers that are super familiar with the arrow function. Having not already been familiar with it, it could create a visual stumbling block where the person starts to parse it and then realizes oh crap, this is a function, they have to go back and sort of visually reparse it and then think, why did it need parentheses here and is this returning, is it a curly brace? Do I need a parentheses set here?
Arrow Function Variations
I want to talk about various variations. We've already seen that if you want to make yourself an empty function or an empty parameter list, you're going to have to use some sort of placeholder. Typically somebody will use something like the parentheses, the open close parentheses like we see here. You also might see people just put in a placeholder variable. Now I really hate that people use the underscore as that placeholder variable, they also like to do it without any spaces, so suggest that idea that this is a variable that I don't care about, so I'll just use this placeholder underscore variable. Some people like use x as their placeholder variable. X is definitely better than the underscore, that's especially true if you're working with the underscore library. Okay if you're working with underscore or lodash, please, please don't use underscore as your placeholder variable name. Unless you're trying to confuse people. Unless you're trying to create visual hiccups. If your goal, like mine, is to create more readable code, please avoid those sorts of tricks that cause more problems for the reader. Okay, so when the case is that we have one variable that we either need or we don't need, and it's a simple lexical identifier, we can just simply list it there without any parentheses set. There will be several additional features that we're going to talk about throughout this workshop that ES6 affords us the capability to do more with our parameters. There are, as a matter of fact, three. There are default values, spread, and destructuring. These are three different sort of things that we can do with our parameters. We're going to talk about each one of those, so don't worry if I just started speaking Green to you. But in all three of those cases, if you use any one or a combination of those three, even with a single variable you're going to have to put a parentheses set around it. So if I used the ... for example, the gather operator on my parameter, I've got to have a parentheses set around it, okay. So there's our first set of variation is that we can either see open close parentheses hanging around, and by the way, open close parentheses, you know, the problem with that is your placeholder for your parameter list looks an awful lot like a function invocation. So people see that and they normally think, oh something just got executed, but wait no, this is the parameter list. So see, there's already more visual hiccups than what we had before with writing out the word function. Okay. So any one of our nonstandard things, also if you're going to have multiple variables, like for example x and y, and your arrow function is going to do something perhaps with the x and the y, if you have multiple parameters, they have to be surrounded by a parenthesis list. Little side note, this is not standard yet, there is a proposal to have what's called headless arrow functions, meaning if we don't need a parameter list, we can just completely leave it off, okay. That sounds awesome until you then realize that's yet another variation in syntax where somebody's have to backtrack visually to say, wait a minute, was there a parameter list or was there not? Because they're going to see something like this (Typing), that's not confusing at all to anybody is it? Okay, so we're already starting to collect up quite a few of these syntactic variations, and we're not done. Alright so multiples, zero or non-standard parameters, you're going to have to have a parentheses set. Now let's go to the function body. The function body in this sense is called a concise function body. You notice I'm just doing the constant 3 just to make it easy for me to type over and over again so I don't screw myself up. But it's an expression, not a statement. In the concise form, it's an expression, not a statement, meaning all legal expressions are legal as function, as arrow function concise bodies. Okay, all legal expressions. Now in some languages statements are expressions, but for historical reasons in JavaScript, statements are not expressions. So there are certain kinds of statements that you might want to just put in a concise body that you're not allowed to. And I have run into the most common of those places where I get annoyed by this statement versus expression problem with arrow functions is the try catch, because I would really like for try catch to be something that I could wrap around the body of a concise method, of a concise arrow function, but I can't because try catch is a statement. And if you have a statement, you're going to state, you're going to have to wrap your statement in a curly brace pair. Okay. So if I needed to do something like, for example, try catch. Now I know this is silly with my 3 example, but just for, just to keep things, that's not valid, so unfortunately I'm going to have to put a curly brace pair around it, but I'm going to have to remember that that thing's a statement and it's not valid. Other examples of places where I've wanted to do this if statements, for loops, those are all statements that it might be nice to have in the concise form. And by the way, we do concise if blocks all the time where we put if if if, and that's totally valid and totally legal, but it's not valid as the concise body of an arrow function. Because these are expressions, not statements. So we're going to have to wrap that curly brace pair around it. Now, let's talk about what that non-curly brace pair is doing for us, it's implying a return keyword. This is implying that the value 3 is the return value of this function. Guess what happens as soon as you put a curly brace pair around something? No more implied return. So you're going to have to remember, if you run into one of those cases where you've got to wrap a curly brace pair around it, you're going to have to remember, oops got to go back in and put my return keywords in. Yet another set of variation, so now if for whatever crazy reason you need to have some curly braces around a thing, you're going to have to remember to go put in that return inside of your block, or it's not going to get returned, there's no implicit return. Okay, yet another set of syntactic variations that somebody running through your code, they're going to have these visual hiccups like, wait why does it need a return here and not a return here? Okay. Yes? If you have a single parameter, can you still put the parentheses around it? It is legal to still put the parentheses around it, but the ES6 aficionados will jump out of the shadows and sneer at you, why you'd put those parentheses, you don't need parentheses, don't you know you can leave off parentheses? They have jumped out at me before, so I'm just warning you, they're there, they're in the shadows. But if you want to return an object, then you wrap... We're not done yet, you're absolutely right. I love that my students always like to be a half step ahead of me, that means I'm doing exactly what I ought to be doing as a teacher. So what happens if the return value that you want to return say needs to be an object? Well you can probably spot the problem here, the curly braces there are not going to be treated as an object value, they're going to be treated as the curly brace pair around making it not a concise body. So if you want to make it a concise body, you might think, oh I can just do another curly brace pair, but no, no, no. You have to put parentheses just around the body to be able to return an object value. A lot of different variations here. In my opinion, the variation in syntax is a detractor from that overall value proposition that we seem to be making that, oh my God, it's going to be, I'm going to write code that's so much cleaner and readable because I don't have to write the word function and the word return and all those crappy curly braces and all that other stuff. If that's a value proposition, we also, to make an apples to apples comparison, have to also add in the negatives. And the problem is, a lot of people only talk about the positives. So I want you to make an informed opinion holistically on the whole issue. Okay. The choice that we make to write these arrow functions does kind of take us down this path of, but what about readability? Now, there are a few other things that I want to point out about the arrow function. I don't want to dwell too much on this, because it's one of our not right parts. But I do want you to understand arrow functions. A few other things to point out about the arrow function, they are anonymous, they are syntactically anonymous. There is no way to give a name to an arrow function, syntactically. You can't put like, you know, some extra name there before or after the arrow symbol or something like that. Now most people have probably not thought too much about, well what difference does it make if it's a named function or not? Let me give you some examples of places where named function expressions are actually a whole lot better than anonymous functions. Anytime your function needs to make a self reference to itself, typically that's for recursion, but there are other cases where your function might want to make a self reference to itself. For example, an event handler that needs to unbind itself after it's run one. It needs a self reference to itself. If it's an anonymous function, you don't have a convenient syntactic self reference, so you're relying upon some crappy outer variable that might be pointing to something. Okay. Another example where anonymous functions fail is the fact that anonymous functions show up as anonymous functions in stack traces. So we see stack trace with 12 lines of anonymous function and we're in minified code, so they all say line 132, character 32,712, and we have no idea what that is. So if you've ever had a debug stack trace with anonymous functions, you know that anonymous functions are less helpful than if the function has a name and if it's not just a name like foo, but a good useful name like handle clicks, now you start to get in your stack trace a more reasonable idea of what it's doing, okay. So there are downsides to using anonymous functions. Now the astute in the audience is going to think, oh I've heard of this thing, anonymous functions get what's called named inferencing. Say I have this, I have a var foo = and I do one of these arrow functions, like that, it's an anonymous function expression assigned to the variable foo. If you run that code and ask, what is the name property of it? You are in fact going to get the string foo. So all of the sudden it starts to look like this isn't an anonymous function, it's a function with the name foo. That's called name inferencing. There's about a half dozen rules in the spec for how the spec can make a guess and inference as to what name you probably want to refer to that function by based upon where that function expression gets saved, okay. Sounds like our safety net, right? Sounds like, okay great, I'm going to have all my arrow functions, but they're going to be non-anonymous because they're going to get name inferenced. Except the problem is 99.9999999999% of all arrow functions you're ever going to write are going to be arguments to some function call. Like for example, like that. This is the one case in syntax where it doesn't have any name inferencing because how could it possibly guess what you want to call it? It doesn't know what the parameter name of a foo function is necessarily. So guess what happens? It doesn't name inference in this case. So in all the places where you're probably going to use arrow functions, you're probably not going to get name inferencing like you think. So they're going to stay perpetually syntactically anonymous.
Promises and This
I get people all the time saying oh but, I write promises and man I hate doing stuff like this, I hate doing, you know, I hate having to write out (Typing), I don't want to have to write that, that's way too long, I want to just write p.then. That's so much nicer, right. In the cast where we make a simple return value, you probably think to yourself, no big deal, right. It's much shorter to just throw in an arrow function. What do you think happens if v comes through as null or undefined? Here's a hint, JavaScript exception is thrown. Because you're trying to do a property access against a null or an undefined value. Okay. So this arrow function is going to show up as an anonymous function right there in your stack trace. So even in these cases, these real simple cases, these little one liner things where you think it's a clear win in readability, the cost comes on the backend whenever something breaks and now you have a worse stack trace to deal with. A stack trace that doesn't give you any help to understand what's going on, you have to click through the code to go figure it out. I think you ought to use name functions and I think you ought to give this some kind of name like extractID. The reason I think you ought to do that is the extractID name is going to be used in the stack trace, that'll make your code more debuggable, but it also makes this code more sensible. I don't have to read the code on line 3 to figure out what it's returning because the name right there at the top tells me what it's doing. It tells me that it's extracting the id. So the next time somebody makes an unambiguous, absolute statement that arrow functions are more readable than named function expressions, just ask yourself how true really is that? This goes for promise.thens. This goes for .map calls. I use named function expressions, not arrow functions for them because I prefer to have the syntactic consistency. I prefer to have self reference if I need it. I prefer to have self explaining code with name expressions. That's just me. I'm crazy that I like to make my code a little bit more readable. Plus the one from the channel. Okay, a few other people maybe have _____ with this problem. I always start out with the code on the bottom, and then eventually like I need to put some logic in there, and so I wrap it with parentheses, and then I'm like, oh cool, I want to return an object, so you know, I'm just throwing in different syntax rules as I, the code develops. My little joke that I tell people about arrow functions, if you write an arrow function and it doesn't parse, just start wrapping parentheses and curly braces around stuff until it does. That's how you know how to do it. I want to pull up, you see here in the shot, you see I've got a book series called You Don't Know JS. People know a lot about those books that I've written. The ES6 and Beyond was the last book in the series. In chapter 2 of this book, I cover arrow functions and I talk about a lot of these things. Half as a joke and half seriously, I've included a flow chart on how to decide whether or not and in what way you're going to want to use arrow functions. So let me, I don't know why this isn't zooming correctly, something about the video. It's going to be a little bit hard to read if you're looking on the projector, but you can go find this in the chapter, it starts off with, are you really sure you want an arrow function? But then it just keeps going and it's like, do you have exactly one? Is it a simple parameter without anything? Are you returning a value? Do you not need to return a value? Is it an object value? By the end, you get down to this and you have made your decision, congratulations, go forth in arrow function. But take a step back holistically and ask yourself, is this the decision chart you really want to run through every time you have to write or function? Or how about just write the function keyword? Because I bet you can run that faster, type that faster than you can run through all of these steps. And I bet the readers of your code are going to be able to read it more quickly. So does that mean I'm saying the arrow function is entirely bad and should be just thrown out of the language? That's not at all what I mean. There is one specific example where the arrow function actually shines. It's specifically, I think, the reason why, it's the thing that gave it enough weight to make it into the language. I wasn't on the committee, so I'm not speaking officially, but I'm just saying, personally this is the reason why I think it deserved to be included in the language, even if it's not my general arrow is the new function replacement. And it's for cases like this. Let's say that I have a function foo that at some point says setTimeout and it does another function here and let's say that this is the property of some object, and what I want to do is have some property on here like id: 42 and then I want to be able to say after 100 milliseconds I want you to print out this.id. Okay. So I want to be able to say something like obj.foo and after 100 milliseconds I want it to be able to print out the value 42. If you've ever dealt with the this keyword before, you know exactly what's going to happen here, the function that's invoked by the setTimeout is going to be invoked using the default binding rule. Check out my This & Object Prototypes book if you want to know more about the this keyword, but it's invoked with the default binding rule. As a matter of fact, actually by spec, the setTimeout invokes it explicitly in the window context, but the same result is the this keyword is not pointing at obj, the this keyword is pointing at our global object. Not what we want, so we're going to end up getting undefined instead of 42. That sucks. So the way people mostly solve this is to say something like var self = this, and then they use self here. First off, that's a terrible variable name, because the this keyword does not ever in any circumstances that you ever run into refer to the function itself, it just doesn't. That's a bad variable name because we couldn't come up with anything else, we were uncreative like we are. You should have been calling it context because that's what it really is, it's a dynamic context, it's an object context. So if you called it context, at least I wouldn't be able to complain about the variable name, but the other problem here is you've gone to the trouble to have all this this based coding and now you've fallen back to non-this based coding, lexical variables. The real solution to this problem or the solution that embraces the language the way it is is to use the .bind utility. Nobody likes to write .bind this all over the place. I fully understand why that sucks. As a matter of fact, somebody had proposed the double colon operator to try to get away from having to type .bind. But this is that place. If you've been writing var self = this in some part of your code or .bind this in some part of your code, this is that place where the arrow function shines. Because the arrow function doesn't have a this keyword in it. It can't be bound to a this keyword, so if you reference the this keyword inside of an arrow function, it will automatically lexically go up one level of scope and use the this keyword in the surrounding scope. It doesn't have its own this keyword. So if all we did was just change this into an arrow function, with or without those curly braces, I'm going to put them there just for clarity, but if we took that out, sorry my indentation is all messed up here, all we did was make it arrow function, now all of the sudden the this keyword starts working correctly, or working the way we would assume it to work. The arrow function doesn't have its own this, so we automatically get a lexical this. That's where arrow function shines. If you're using this base coding and you're running under those var self = this or .binds, you have my full faith and support to go start using arrow function in those places. You're going to have to be careful when you're mixing arrow functions and non-functions. Okay. And the exercise I'm about to have go through is going to teach you that lesson. You're going to have to be real careful about exactly what scope is the arrow function going to adopt from, but arrow functions by themselves really do shine, in this particular case they solve this particular problem. Actually, we don't even need the curly brace here, the curly braces in this particular case are optional because the console.log method call is also an expression. So there we wouldn't even need it, we'd have to take out the semicolon. Okay. With or without the curly braces, it's going to adopt the this keyword from the outer scope, so that's why you ought to be using arrow functions, but I don't necessarily think that they shine in terms of general replacement for all function usages.
Exercise 0: The Arrow Function
So that's my case for and against the arrow function. I now want you to practice. So I want you to pop open ex0, it's 0 because I was too lazy because I was too lazy to go back and renumber stuff. I inserted this just before the workshop. This ex0, big old hairy mess of foo bar stuff here, this is the file that you have. So you can know that there's a whole bunch of functions in here. Functions used in various different ways, functions used as IIFEs, functions used as function declarations, functions used as function expressions that are returned, functions used as callbacks passed to a map or a setTimeout. Some of them have a .this reference in them. A whole bunch of functions across this. Your task with exercise 0, go crazy with the arrow function. Right, start rewriting all of your stuff using the arrow function. And by the way, I didn't go over this, but the thing that people that love the arrow function love to do is they also love to avoid the curly braces at all costs. So let's say we had a function like this, let's say we had a function called foo that did an if statement, if x is greater than 5, return x else return 1, for example. Okay, very simple example. When somebody goes to convert that to an arrow function, now we have an if statement, you're not going to be able to use an if statement in the concise body, but we don't want to use curly braces, right? So the way we do this is we replace the if statement with a ternary because that's an expression and we can use that. So when you see people write that, they'll say var foo = x and then they'll start using a ternary, and oh by the way, we also need to write all of our arrow functions all on one line, for maximum readability sake, write them all on one line with no indentation or new lines. So x = x > 5 x or 1. Totally readable, right? Nobody has any trouble parsing that whatsoever. It's like the good old days of Pearl. So the point I'm trying to make is that whenever you start using arrow functions, you're going to want to pull out every kind of grammatical trick that you can to avoid having to write curly braces. I mean, I'm talking about putting like variable declarations as unused parameters in your parameter list, using the comma operator as a part of a way to put expressions, multiple expressions in one concise expression. I mean every trick that you can think of. So go crazy with exercise 0. Try to make as much of that. By the way, if you run this code, if I run this, what you'll notice is that it returns true at the end. So your code needs to preserve the test case that's built into it, it needs to continue to return true, if it doesn't you failed, okay.
Exercise 0 Solution
Rather than take a whole bunch of time to go through and type all of this stuff, I'm simply going to walk you through the fixed version, which is included with your exercise. And let's talk about a few things that are going on. First off, I've taken the IIFEs and made it an arrow function on line 1, I'll zoom in so it's a little easier to read. And you'll notice that the IIFEs in the other one didn't have any variables in it, but I took advantage of being able to declare variables without statements by putting variable names as unused parameters in that IIFEs, and they're unused because down here at the bottom the parentheses pair that executes it doesn't pass anything in. So those are unused variable names that I can then start assigning to. So for example, I then open up my arrow function and I open up a parentheses set because I'm going to put multiple statements together or multiple expressions together using my comma operator, and because of the operator precedence of the comma operator, if I did that without the parentheses set, it would bind less tightly than the arrow. So I have to wrap the multiple expressions that I'm going to string together with commas in a parentheses set. So, the first expression is I'm going to say foo = and I'm going to make foo equal to another arrow function. The reason for that is because in another expression down here, this one, which is again part of that outer parentheses set, this expression calls foo, and then I set and then I set. So here it looks an awful lot like a function body, but I'm using commas instead of semicolons because these aren't statements, these are expressions inside of a comma list. Okay so I wanted to get rid of my curly brace pair and all of that. So each one of these individual ones is an expression rather than a standalone statement. So my foo function is an arrow, I take advantage of being able to declare my x, but also to declare a y because I'm going to want that y, the one that was in its own stand alone declaration, I'm going to want that y, so I'm going to use that y and I also use a trick that I haven't shown you yet, which is default parameter values to go ahead and make the assignment of it instead of having to do a separate one. Now here's an interesting trick, I use a regular function, and we'll come back to why I use a regular function here, but question for you, is that a function statement or is that a function expression? Who thinks they know? The fact that I didn't get an immediate answer is kind of the point. Okay, because it looks an awful lot like an expression statement, but because it's part of a, because it is the concise body for an arrow function it's actually a function expression. It's a named function expression rather than a function statement. Anyway, I have a named function expression, which is the concise body for that particular arrow function. It would have taken a z, but I also took advantage of baz and obj unused parameters for my variable declarations. Now because I have a regular function curly brace, I don't need to do any ternaries here, I can just do my regular if else. But we're going to, don't worry, we are going to use ternaries here in just a moment. I do need to do a return statement and I call my z.map, oh here's another case where I can use an arrow function. So I thrown in my arrow function here, this is the arrow function, but I also assign that arrow function to a baz because assignment statements or assignment expressions have the value that is assigned as the return so I can assign and pass in the value all at the same time, were I not having to have a separate statement, and the reason it needed to be named is because I needed to refer to it recursively by name, so I needed a name that the arrow function was assigned to. Now down here, these are regular old statements, but I now use an arrow function and this is my concise body, again with parenthesis around it and a comma operator to separate my two statements, and there's my this keyword. Taking advantage of the fact that this arrow function won't have its own so it will come to this outer function to get its this keyword, right. Finally, I have to make sure to do my return obj. The rest of these should be pretty straightforward. Let's look at the setTimeout. Again, I use an arrow function. I use the open parenthesis set here. Didn't need any variable declarations. Because console.log is a function call, it doesn't need any kind of curly braces around it because it's an expression call. So I can just simply have that as the body. And finally, my reduce, my reducer uses an arrow function, it's got two parameters so I need to list both of those with a parentheses set around it. Okay now the last thing I want to point out about this exercise is, and this was the thing I messed up, and it took me like 10 or 15 minutes to figure out why I'd messed it up, because I had made this into an arrow function first. It seems reasonable, right. Why not, if we're going all out, let's go ahead and make all these arrow functions. And I made the example and I still forgot the fact that you can't use a .call on an arrow function, well you can but it has no effect, because you try to bind the this context of an arrow function, it doesn't have one, so it's just going to ignore that .call that you make. So if you have a place in your code where you need to make a .call or a .apply or a .bind or any one of the ways that you do with this binding on something, if you need a thing to have a this binding, the trick here was I needed this thing to adopt its this context from an outer scope that had a this. But if the outer scope of this arrow function had been another arrow function, it would have had to just keep going up and keep going up. So you're going to have an arrow function inside of a regular function if you wanted to be able to adopt at this context. The point I'm trying to make is, you're not going to be able to replace all functions with arrows and you're going to have to be really careful about which ones can and which ones can't and under what kind of conditions. Okay. Now I deliberately make this big and hairy and messy just to illustrate all those different things, probably your code isn't going to be quite so bad, but you want to be careful as you're writing your code. Any questions? It looks like there's a question in chat, can you explain lines 13 and 14 with the parentheses set? Yep, so this is an arrow function, it's got a concise body, which we start on line 13 and finish on line 14. Because there are two separate assignments that I want to make, first is object.length = 1 and the next one is object of 0 = this.w. Those would normally be two separate statements that I would semicolon and put inside of a curly brace pair, but because I'm arrow functioning here, I want to just use one parentheses set because if I took off the parentheses, the comma operator binds less tightly than the arrow, which would have meant that only this would have been the arrow function. We wanted the whole thing to be the arrow function, so we have to bind the comma inside of the parentheses set to get that whole two lines to be the concise body. Okay, hopefully that answered that. Any other questions that I missed in chat or right here? Yeah, this is along the same lines, can you just state what it is in JavaScript that makes it so that you can just put a comma and turn two different statements into one expression? The comma operator is defined as being able to do exactly that, which is to take, it's not two statements, it's two expressions that can be chained together. Any general place where I had, you know, foo of 3; bar of 4;, it's entirely valid JavaScript for me to do that. That's JavaScript ES0, the comma operator. Okay, like I said, when you're ES6 arrow functioning, the thing you really want to do as much as possible is pull out every kind of weird esoteric grammar trick that you can to get rid of all the curly braces and semicolons and stuff. So comma operator is a favorite of writing arrow functions. Can you explain the difference between expression and statement, just to clarify? Um, I'm not going to go fully into that, check out my Types and Grammar book, I think it might be chapter 5 I cover the difference between expression and statement. In English grammar, the English language grammar, the closest analog is the difference between a sentence, which is a statement, and a phrase, which is an expression. Sentences are made up of phrases, statements are made up of expressions. But in JavaScript, a statement can't be used in place of an expression, but an expression can be used in place of a statement. And do you go into that in your advanced JavaScript course? I do also cover that in the advanced JavaScript course here on Front End Masters and in the Types and Grammar book. Another question. Another question, if the setTimeout is not returning any value, would it be a bad practice to use curly braces in the arrow function on line 12? Curly braces here, would it be a bad practice to throw curly braces around it? Again, what you're going to have is you're going to have the ES6 police jump out of the shadows and scream at you that you're supposed to be avoiding curly braces wherever you can. There's nothing, sorry the curly brace would have been here, there's nothing grammatically wrong with it, but stylistically if you're going all in on arrow functions, usually you're going to want to go all in on arrow functions. (Typing) The ES6 police. So in your opinion, if you're using arrow functions at all for what they're intended for would be the replacement of the this operator in that instance? The place where it really actually was useful, in my opinion, is right here. That's where the arrow function was actually justified, all the other usages of it I think are kind of dodgy, my own opinion. Yes? Another question from the chat room. We've got an active chat room today, I love this, it's great. Yeah, yeah. Well this is real stuff, right? Yep. Is baz a named arrow function on line 5? Baz, a named arrow function? Uh, no. There's no such thing as a named arrow function. Baz is an arrow function that gets assigned to the baz variable. That's what's happening on line 5. There is an arrow function here, the part that I've highlighted, that then gets assigned to a lexical variable called baz, which was this unused parameter from line 3, so it's just assigned to it. Would it have name inferencing? Yes it would have name inferencing so if we were to call out the .name property of it, or if that were to have thrown an error in the console, then it would have said "baz" as the name of it, but there's no such thing as a named arrow function. There is such thing as a named inferenced arrow function, I guess. Okay, follow up on that. So if can be a reference, even when used as a parameter? I don't know what named reference is referring to here. No such thing in my opinion as a named reference. Thanks Mark. ES6 police. If there's not a Twitter account for ES6 police, there should be by the end of my class. Somebody better start it. Is the type of arrow function an object is what Ryan is asking? Is the type of arrow function an object? It's a function object just like a regular function is an object, it just has special syntactic rules to it, a few special behaviors to it. But from the perspective of it being an object, yes it's still an object.
Block Scope
Let vs. Var
What we want to do now is turn our attention to things that I really do think we should be using more. These are more the right parts of ES6. And the first thing I want to talk about is block scoping. So, quickly I'll motivate block scoping. I've covered these sorts of things in many of my other classes as well, so I don't want to keep repeating, but just want to quickly motivate. Let's say I have a function called foo and there's some condition by which I want to swap those two, like for example the case where x is greater than y. Normally you'd do something like this and then you'd say y = tmp. And we've used a var keyword inside of an if statement. Now functionally, meaning operationally, the var keyword here is attaching a tmp variable to the scope of foo. That's what people like to call hoisting. Hoisting is made up, it's not a real thing, but that's the metaphor that we use to describe how lexical variables are attached to their lexical environments at compile time. Okay, so it's "hoisted" to the scope of foo. But why do we put it inside of the if statement? What is going on stylistically? Because this is a thing that people do, they'll put their var keywords in specific locations, even though they know they actually behave as if they belong to the whole function. Well what we're doing here is we're stylistically saying I want the tmp variable to belong only to that if statement. That's what it's for. Another example is when you have a piece of code like this and you do a for var i= and then you have some code inside of the for loop. The var i here is going to be hoisted to the scope of foo. The i is available everywhere, but why do we put it on the for loop? The reason why, at least for most of us, the reason why we put it on the for loop is because we are stylistically signaling to the reader of this code, i belongs to the for loop, don't use it before or after, even though you can, don't. Even though you can use tmp elsewhere, don't. It's supposed to be used only for this if statement. These are the kinds of scenarios under which block scoping makes entirely sense. It makes complete sense in all other languages that have ever had it, and now JavaScript has it. Unfortunately we can't just make the var keyword block scope because that would break a lot of old code. So we had to add a new declarator type. And that declarator type is the let keyword. In both of these cases, the let keyword will enforce the thing that we were already stylistically signaling. That's a really important point, don't miss what I just said. The let keyword enforces, at the compiler level, it enforces what we already stylistically signaled. Meaning, I want tmp to belong to the if statement, now tmp actually does belong to the if statement. If we try to use tmp before or after it, we're going to get an error. If we try to use i outside of the for loop, we're going to get an error because the let keyword is going to enforce that which we were already stylistically signaling. That is now I think you ought to approach this let keyword, to enforce the things that you already stylistically signal. Does that mean that you ought to make all your vars into lets, as some people are liking to say? Right. Well that is the new var, there's a bunch of people on TC39 and this is a strong cult bandwagon that tells us, let is the new var, var is a code smell, these are all blog post titles that have actually been written, right. So should we be using the let keyword in places where it doesn't actually enforce some sort of block scoping, like for example at the top level of our function? You can. I don't think you should. This is the place where I start to diverge from many of my peers who teach JavaScript, and I respect their opinions, but I think they're wrong on this part, they're missing the fact that the var keyword actually tells us important stuff. The var keyword tells us, when used in those positions, this is a variable that I intend to use across the whole function. The let keyword doesn't tell us that. It's position tells us that, but the let keyword itself does not visually signal what our intent is. If you use the var keyword here, it is now telling the reader of this code, I know what I'm doing and I'm intending to use z everywhere. So my personal take is you ought to use both let and var. Use vars in the places where you intend to use them across lots of scopes, use lets in the places where you already stylistically saying I want to contain it to this block. I think that's what it's good for. And so I don't believe in the let is the new var, I believe let and var. Let is the new helper var, it helps you enforce the places where you wanted var to be block scoped all along. Yes? Question from room. Sure. I've heard "only use let in places where you want the value to be mutable, const everywhere else." We're going to talk about const in a little bit. I'm not there yet, so we'll come back to that question. Okay. I'm just talking about how you decide and, again, just like with the arrow function, I'm trying to present you with the different cases here because I want you to critically think, as engineers I want you to analyze, don't just follow some lint rule. Think about which one is more appropriate, which one communicates better. Okay. So some other places where I think the var keyword is actually superior to the let keyword, okay, because there are places where I think that is true. Let's say that I had a line like this, let's say that my line of my var z here was actually calling some other function like bar to get its value back, okay. No big deal, I'm following the ES6 bandwagon, I put a let keyword on it. It seems great because that z is available to the whole thing. Now maybe I'm the only one that's ever had a JavaScript error in their program, but I get JavaScript errors in my programs. True confession, okay. And when I get JavaScript errors, one of the ways that I narrow down those things, of course, I try to use debugging tools, but one of the ways I narrow those things down is to put try catches around stuff. So let's just say I'd narrowed down that there was something happening with the statement, I want to figure out if this is the place where the error is coming, so I just casually come in and throw in my little try catch around this. Do you spot the problem? You see, what I did is I try caught one error, but then I created another error, that's one of the big sins in all of programming, whenever one fix causes another problem. What's my another problem? My another problem is now z doesn't belong anywhere else, so even if I figure out what's wrong here, I'm going to get another error that says z is not defined, I'm like what the hell? Z is clearly there. Oh, crap I forgot I had a let keyword. So whenever I'm doing that quick little like debugging, I'm going to have to remember in those places, oops this thing can't just be try caught, I need a var there. Or you're going to do what some people say, which is, oh no, no, no, what you should have done is you never should have had those two together. You should have had the let z separate and then the z = bar in its own separate thing. So you should have had those separate, there's your problem solved, right. This is the argument that's often made, there's your problem solved. Now what I'm going to say is this. I think, this is personal taste, I think that one of the things that aids readability in my code is when the declaration, the first usage of a variable, is as close as possible to the declaration of that variable. By as close as possible, I prefer them to be on the same line. But if they can't be on the same line, I really want them to be as close as possible, okay. In this particular example, they're only separated by 2 or 3 lines, no big deal, but as things go, as our code evolves, you might start to see more separation between those. It could be 5 lines, it could be 100 lines, it could be 1000 lines between the place where it was declared and the place where it was used. And as soon as you start to create more and more visual separation, especially more than about 3 to 5 lines, now you have a visual readability problem because somebody's going to look at that line z = bar, say wait a minutes, where does z come from? They're going to have to go hunting for that let z. And they're going to have to make sure that they didn't cross any curly brace boundaries, that they really found it at exactly the same scope as the place that it was. So I think you ought to use your variable declarations as close as possible to where you're going to use them the first time. That also means that if a variable is used in two very different places in the code, I'm going to shock you and say I think you ought to declare it twice. You ought to declare it up here where you use it and then you ought to re-declare it 1000 lines later when you use it way down here. But you can't do that with the let keyword, you can't re-declare with the left keyword because you're going to get an error. This is one of those places where, in my opinion, the var keyword shines because it allows me readability-wise to put that little notation down there at the bottom, hey I'm just reminding you the z is coming from this outer scope. So I put the var z down there at the bottom. Another place where this shows up is in if statements. I regularly do this in my code. I'll have some kind of condition, it doesn't matter what the condition is, and I'll have a whole bunch of variables that I create and initialize based upon this condition. So I'll say var w = and var 4 = , over and over and over again, and then I'll have an else some other condition and I'll have a different set of initializations for those and maybe a whole different set of logic that's happening in that particular case. And then I'll do another else and I'll re-declare them again and again and again. Do you spot the problem if I use lets here? I would be block scoping those to those if statements. These are not really if blocks in that sense and they're not really block, they're not really scopes in that sense. They're just collections of assignments that I want to conditionally happen. Don't go using some crazy set of ternary stuff to do this, just use the var keyword. And if you want, go ahead and be super redundant and put the var keyword declaration also outside of the it statement. Because what you're trying to do really is not to write as few characters as possible, to write as many characters as it takes to get your message across because you want to communicate better, at least I hope that's what you want. That's what I'm trying to get at here. Okay. So I think there are places, operationally and stylistically, that the var keyword and the let keyword can be used together and they have different purposes and one does not replace the other. We ought to use them together.
Closures and Explicit Blocks
One last little note on the for keyword specifically, this particular usage, as it applies to closure. If you're not terribly familiar with closure, I cover that in my advanced JavaScript class, also the Scope and Closure book. Closure is allowing a function to remember variables from outside, even when it runs elsewhere. So let's say you have some example, like you're setting up some click handlers on some buttons, so like btn +i.click, and then you're setting up these click handlers that are supposed to print out (Typing) what's been clicked, okay. Sounds all well and good until you realize there's only one i when we use a var. If we have a var there, there's only one i, so every one of those click handlers is going to close over the same i, they're all going to print out i 10 because that's the value of i at the end of this for loop. Unless it gets changed to something else, right? But if we make a let keyword, is that making only one i? Turns out, there's another little subtly of JavaScript ES6, that it actually creates a whole new i for each iteration. So there's going to be 10 separate i's created, each one with the correct value and our inner functions here are going to be closing over that i value. Functions or arrow functions, either way, they all behave lexically with closure the same way. They're going to close over that i value the way that we expect them to close over the i value. So if we click button 3, it's going to say i3. Okay. Is that only if you have the let keyword within the for loop? It's only in the for header that it will automatically create it. Another way of getting around that, if you wanted to use a var in here for some reason, for some other reason, another way of getting around that is to make your own inside of the for loop, so let j = i and then close over j. Either way you'll accomplish the same thing, okay. So block scoping can actually be useful for dealing with closures in the cases specifically of for loops. One last thing before we move on to const. Again stylistic suggestion here. There was a form of the let keyword in Firefox more than a decade ago and it looked like this. Instead of having a let keyword like let x = 2, you did let (x = 2) with parentheses and you made a block. And now this block had x inside of it. It's called the let block form instead of the let declaration form. Now I actually think this one is more preferable syntactically speaking because what we've done now is create an explicit block for no other purpose than being a scope. The let declaration is actually an implicit form because it hijacks an existing if or for loop and turns it into a block of scope. Meaning, if you have a big old thousand lines of code and you're trying to figure out, is this thing a scope? You're going to have to scan through every line of the code and see if you find a let somewhere. It's implicit, right, it's more mental tax for you to juggle to try to figure out. It's not impossible, it's just more work for you to do, and I'd like you to do less work if possible, and I'd like your readers to do less work as well. So I think the explicit form here is a little bit more preferable because it makes very clear this isn't doing anything other than being a scope, even if I was doing that inside of an if statement, I would still think having an explicit block is better than an implicit block. Okay, that's my own personal taste. Unfortunately this particular form was rejected by TC39, not just rejected like nah, but rejected with prejudice like you are a bad person Kyle for asking us to put this in the spec. And I'm not making that up, there's a discussion thread where they yelled at me for asking to put this in the spec. So this doesn't exist in the spec, but the next best thing is to do what we've done forever in every other language, which is to just open up a curly brace pair and make an explicit block for that scope. Every other language has used curly braces, we've never seen that in JavaScript, I think you should. I think you should put curly braces and make your explicit blocks. The only exception I'd make to that is that for loop with the for let i thing because that's so helpful for helping with closure, but in all other cases, I think being more explicit is more helpful. So this is a syntactic form that allows us to an explicit block, and yes, by the way, I intentionally put it all on the same line stylistically because I want all of my declarations to be at the top. Because there's this thing in ES6 called the TDZ, the temporal dead zone, I'm not making that up, where if I had let x = 2 and before it I tried to say console.log x, which sounds silly, but it happens all the time, we accidentally try to reference something, some other programmer comes in and puts in a piece of code, this is actually going to be an error. You're going to be getting an error because x doesn't actually exist in an initialized form that we can use yet. Okay. So because I don't want you to run across TDZ errors, I think you ought to put your declarations all at the top of your blocks. Yes? So with the temporal dead zone, it actually throws right? It's not like x is undefined, it actually throws an exception? No, it literally throws an exception, it's a reference error, but it's called a TDZ error by the spec. Right. And then my previous question would have been, did Firefox remove their old form of let then or is it still in Firefox today? I actually don't know whether Firefox removed it. Normally Firefox reserves the right to leave stuff in, but let's just ask whether or not they did or not. It looks like they probably removed it. I guess that discussion thread was pretty clear it's never going to happen, so they thought why not go ahead and remove it.
Const
So I think you ought to make explicit blocks, not use implicit blocks, that's my own personal take on that. By the way, let declarations can obviously declare more than one, so you could say y = x * 3 or whatever you want to do. Okay, so that's the let keyword, in addition to the var keyword. Any more questions on let before we move onto to const? Const is another block scoping operator that was added to the language. So in this particular case you could use it in the place where you had a let, it's a block scoping declarator, so it's kind of like a cousin to let in that sense. But what const does is something a little bit different. And it's probably something a little bit different than what you might normally assume. So let me just ask for a moment, what do you all think const x = 2 is actually creating? Anyone? Keeps you from reassigning x to something new. Good answer. Anybody else have a different answer? Talking just conceptually or like under the covers, what does it do? You tell me? I would assume it's making a getter that has no setter. Oh, I'm not necessarily talking about implementations, I guess what I'm really asking is, what's the behavior in our code? What are we created? Immutable value, but if you assigned again it wouldn't change. So some people say immutable values, okay. So we've heard can't be reassigned, we've heard immutable values, anybody else have a different explanation for when you tell somebody about the const keyword, what are you usually saying? You're usually telling somebody I'm creating a what? A constant. So what is a constant? Value doesn't change. A value that doesn't change, that's how a lot of people describe it. Okay, only one of the answers that I've heard is actually accurate. All the other ones are well meaning in spirit, but off base in terms of what's really going on. So specifically, a constant is a variable that cannot be reassigned. It has nothing to do with the value. But the problem is that word constant is always associated in our developer minds with the value. So when people hear the word constant, what they think is it's a value that doesn't change, it's a value that's immutable, that's what everybody, almost everybody, says when you ask them that question, what is a constant? We'll almost always get that. But really what it is, as this gentleman said, really what it is is that a constant is a variable that can't be reassigned. So this confusion that I'm talking about is not new to JavaScript. The const keyword has been introduced in a whole bunch of other languages before JavaScript, and as Stack Overflow can prove to us, it's confused developers in every one of those communities as well. So much so that some languages actually had to add another keyword, like Java added the final keyword to try to talk about value semantics because everybody kept thinking that const had to do with the value and it didn't, so they added final to say this value doesn't change anymore. Const is about assignment. So put real simply, if I say something like const x = 3, it is true that 3 is an immutable value, but it has nothing to do with const because 3 is also immutable value here. Okay. What's different about this program is that x can't be reassigned. Okay. To point that out even more specifically, if y is some array with values in it, y cannot be reassigned. If I say y = 2, that fails. If I say y = some other array, that fails. Great. But y is most definitely not pointing at an immutable value because I can say y of 0 is equal to 42 and that's going to work all day long because the value itself has nothing to do with the const keyword. It's just the assignment of it. Okay. So there are some people, as a matter of fact many people out there, that are telling developers what you should do is always declare your variables with const and in certain cases when it needs to be re-assignable, change those to lets and never use var. So they go const, let, never use var. My advice is literally the inverse of that. Var, use let where that's helpful, every once in a while maybe use const. Literally the inverse, okay. Because there's a whole bunch of places where the const keyword is going to confuse readers of your code that are not already familiar with the differences, they're going to be confused into thinking this is the value that doesn't change, for example, some developer is going to come along and they're going to see this piece of code and then they're going to see it pass in x and they're going to think themselves, x doesn't change, I know x is still an array 1,2,3, but is that they case? It's not the case because we passed in a reference to a mutable value and foo absolutely can do something stupid like change the contents of that array and a reader of your code might be confused when they see the const keyword and not realizing that. So the const keyword has the potential to lead people down this path of being confused about the value semantic. And I'm not just saying that on my own, I'm saying we've got three decades of experience with other languages that say sometimes developers get confused. Is it learnable? Yes. It is natural and instinctive and intuitive? Maybe not so much. Okay. So the const keyword has this potential problem, but what's it value benefit? The value benefit, supposedly, is that we're communicating, which sounds exactly like something that'd be right up my alley, we're communicating, when we write this line of code on the first line of our block, we're communicating that x will never be reassigned. Okay. We'll get to that question in just one moment. Just let me finish this thought real quick. We are communicating x will never be reassigned. But let me ask you a question, let's imagine that this is inside of some block somewhere. Okay. And there's some other code in this block. You've probably heard before, people tell you, stylistically you should keep your functions and your blocks really small. Okay, most people tell you, if they're of that opinion, they'll tell you your blocks should never be more than like maybe 5 lines, 6, 7 lines of code, right. They want you to keep things small. There are people that say a whole function shouldn't be more than 5 lines. If it's more than 5 lines you should split it out, right? We hear this all the time. It's nonsense, but we hear it all the time, right? So let's go really crazy and let's say that we wrote a 10 line block of code, I mean I know that's just way out there in outer space, but let's say we wrote a 10 line block and on the first line we said const x =, and we're supposedly communicating to other developers x is not going to be reassigned. But let me ask you a question, let's say that that block is within the program we've got a million lines of code. How many lines of that code can affect the assignment of x? Is it a million? There's only 9 other lines in that entire million line program that can actually change the assignment of x, because it's block scout. Even if we pass x somewhere, the code inside of foo cannot affect the assignment of x. It can affect the value, can't affect the assignment. So let me just summarize it this way and then I'll get to the question. The const keyword is saying I'm not going to reassign this variable in the next few lines of code. I prefer to communicate that by just simply not reassigning it in the next few lines of code. Because the const keyword, because of all the confusion potential, doesn't actually help me with that communication. I just don't reassign it and then I've communicated I'm not going to reassign it. But there's another problem here, which is that whole thing that values can go other places and get mutated, that's a big deal. Const doesn't help us with that. If you wanted to make sure that somebody couldn't change it, you don't need the const keyword, what you need is Object.freeze. That's been built in since ES5, that's not even an ES6 thing. Breaking news, 7 years ago we got this utility called Object.freeze, it makes that object at the shallow top level, it's a shallow freeze, it makes it read-only properties, so in effect it is an immutable value. Okay. Not a real immutable value in terms of performance, but poor man's immutable value. So if I wanted to communicate to some developer that x is not going to be changed by foo, I could just as easily do that with var x and I would say have less confusion potential with them if I use the const keyword by saying Object.freeze.
When to Use Const
The question was about using const for immutable values like strings and numbers? I'm not 100% sure what the question is actually asking. It's Mark K, about 3 minutes ago. Okay, I think the question is where am I going to use const? The place where I think you should use const is where you are already doing stuff like this. If you did something silly like var PI = 3.14, you have my blessing to go change those all to const because those really are mutable values that you don't intent to reassign and if you want to put const on it, that's no big deal. But I don't think you ought to go do like const f = arrow function or anything like that because I think you're just inviting additional visual clutter that's not actually communicating what you think it's communicating. Okay. Yes? What's your take on using const for function names? Like let's say you're creating a utility function and you're calling it add sum or something? Like I just not, I would not do const add sum equals and then some function. I'd just make a function declaration. So there are relatively few places in my code where I have actual constants like this. But in those places I endorse using the const keyword. I don't think you lead off every one of your declarations in your code with a const, because I think you're inviting unfamiliar readers with your code to potentially have some visual stumbling blocks. Is there any benefit of using const on arrays then or objects? I don't think so at all. As a matter of fact, this is a little controversial because I've had debates with people about this before, but saying something like const x = array 1,2,3, let's say that this was a really huge array, like it was 100 megabytes of memory. And let's say that it was inside of some kind of function foo and I declared this thing here and let's say I set something up like, (Typing) so I make some reference to x inside of my function, I've closed over x, x is going to stay around for as long as that click handler is there, and in our program we're never unassigning that thing. But let's say there's some other condition within our program which is that we would like to be able to garbage collect that array because we're done with it. We'd like that 10 megabytes or 100 megabytes or whatever of memory to be recollected. Normally you would do something like x = null, that's no longer allowed when you use a const. So as a matter of fact, there are places where the const could actually make it worse, you have to fiddle around with your garbage collection sorts of things. In this particular case, since it's an array, you could have done something like x.length = 0, but if it were an object you wouldn't have that, you would to for loop over them and delete all the properties or something like that, super awkward. So there are places where const can actually trip you up, just like there are places where let can trip you up. So I would recommend using the const in the places where it's actually justified rather than just preemptively using it everywhere and then getting tripped up. This is sort of a follow on using const with Object.freeze. I don't think it helps at all to add const and Object.freeze, okay, I don't think that does anything extra. If you want, great, but I don't think it helps anything at all. I think because that comes at the beginning of the line, it's already signaled somebody to get confused as to what's going on.
Exercise 1: Variable Scoping
Let's open up exercise 1. You notice that I've created some functions here and I've created some for loops and stuff. Here's our test case at the bottom, it needs to assert true, which is the case that the x value of 2 multiplied by 2, so we're saying that 4 needs to be the same thing that's returned if I call the function in this fns array at position 4. So in other words, I'm going to loop over from 0 up to 5, not including, and I'm going to add functions to an fns array. Each one of those functions needs to close over some variable, such that when the function is called it returns that value. Okay. So looking at your IIFE here, looking at your var statements, figure out which ones of these should be made into lets and const and refactor this code and set yourself up correctly with your closure. It should only take a few lines of code. So we'll just take a 3 minute break and then we'll talk about the solution.
Exercise 1 Solution
So let's talk about exercise 1. Here we started off with a var x = 2. We can kind of see in the scope of this program, that's sort of being used like a constant, so that's a place where you could make the argument that it should be a const. Fns is definitely not going to be treated as a const, in the sense it's not going to be treated as a constant, even though it might be reassigned, we're going to be adding to it, so I think it be super confusing to declare fns as a constant and then later in the code start adding to the contents of it, okay. You can, but I don't think you should. So that's why we're going to go ahead and declare fns as a var, because we're telling someone hey this thing is going to be reused. And I use a var here instead of a let, again, because fns is going to be used across multiple scopes. Alright. Now in the previous example I had an IIFE, and the only purpose of this IIFE was so that I could make another var x, which was sort of my constant in terms of how high up to go. And there would be a collision there if there wasn't a scope. Well I can do that with a block, which I'm doing here, lines 4 through 10. And again, I can use const here because x is actually sort of being used as a constant in that sense to tell us what to run the loop up to. I'm going to do a for with a let keyword because I know I've got closure involved and I'm going to want to close over an individual i for each loop. I'm going to add these functions if into the fns array and just simply close over that i variable, and that preserves my test case, I end up getting true. Questions about exercise 1? Yes. A question from before, but in a more broad sense, Justin had a question about why ESLint is starting to throw errors for var? Yeah, because there's… I want to save it for after because there's a crowd of people that are under the impression that let is the new var and that var is a code smell, so now they have default linting rules that tell you don't use a var under any circumstances. I just think that's a bunch of nonsense, but there are some people that believe that.
Default Values and the Gather/Spread Operator
Default Values
From here on, we're going to try to do sort of before and after comparisons. So what I'm going to really do is just actually split my screen here and we'll try to have kind of a before and after of each of these features that we discuss. So we'll start by talking about this kind of scenario, which is a function that receives a parameter that we want to give essentially some kind of default value to. And you've probably seen something kind of like this. Anybody seen that before using the or operator, this is kind of referred to as the default value idiom or the default parameter idiom. Usually you see that at the top of the function, so you see one of those lines for each one of the parameters that you want to give some default value to. Now if we, for example, pass in something like foo without anything, we know x is going to end up as 42, but what happens if we pass in foo of 0? Does x end up as 0 or does it end up as 42? It's 42. If you aren't really aware yet of what's going on with your operator, it does a truthy test on the first argument, on the first operand, and if it's true or truthy, it returns that operand, otherwise it returns the other operand. So 0 is a falsey value, meaning that it will fail and end up returning the 42 value. Okay, so we can't affirmatively pass in something that's falsey, like 0, null, undefined, etc. So really technically, the long form, the not cheating form of this, is to say if x is not equal to undefined, then use x, otherwise use the default value. Okay, that's really how you write the full version of that default value idiom. So if we pass in 0 now, of course we're going to get 0, if we pass in null. If we pass in foo without anything, we get 42. If we also pass in undefined, we're still going to get 42. So undefined in this case becomes the empty value and everything else, including null, becomes an actual affirmative value that we care about. Undefined means I'm not passing anything here, it's like a placeholder. Okay. My recommendation is to try to treat undefined and null as indistinguishable, expect in this particular case, we need to distinguish between undefined and null, so that's what this idiom does. Okay. So the long form of that is what we would refer to as the imperative form. We're telling JavaScript how to compute the default value. Rather than leading with the default value. As a matter of fact, the default value is all the way down here at the end, so somebody reading through this code for the first time, unless they recognize the idiom, they're going to have to read through all of that and get to the end visually before they understand what the default value is. So when we set up earlier at the beginning of the workshop this notion of imperative versus declarative, that's what we're going to see here in a lot of these side by side comparisons, the imperative form versus the new declarative form. In ES6 we now have a declarative form, which allows us to specify that default value right inside the function signature. Okay. So we don't need to do any of that imperative processing. We're telling JavaScript if it comes in as undefined or missing, go ahead and apply the default value to it. So in this case, if we pass in foo of 0, we get 0. If we pass in null we get null. If we don't pass in anything, we get the 42. If we pass in undefined, we also get the 42. Okay. What do you think happens if I do this, foo.apply, don't care about my this, what happens if I apply an empty array to the arguments list of foo? What's x going to be? 42. What happens if I apply one with an empty slot in it? 42. It's still going to be 42. In all those cases we end up effectively passing in undefined, which then triggers the default algorithm. Okay. So pretty straightforward. We now have the ability to declare our default values.
Lazy Expressions
Let's talk about the values that we put into that default expression. Most of you probably assume it's just simple stuff like constants, but there's no requirement that it has to be a constant. It can be any valid JavaScript expression, not statement, but expression. Same kind of rules as an arrow concise body. We have any expression that's valid could be there. So what if we made it a function call, like for example bar? That's totally valid. And if there was a function bar here that said console.log and put a little exclamation mark. So the question is, in this particular program with no executions, how many times do you think bar has been called? None until you call foo. There's only two rational answers here, either 0 or 1. So how many of you would vote for 0? It's not been called at all. How many of you would vote for 1? It's called once. How about online? Let's take that poll. I know we have to wait 15 seconds. Let's take the poll online. Raise your hand if you vote for, online raise your hand if you vote for it's been called once versus it's been called 0 times. Or just type it in 0 or 1. Okay. So that's the question, how many times is bar called? Okay. It seems like we're getting a bit of a mix, but maybe a little bit more weighted towards it's called once. Okay. So that's why we talk about this, that's why we ask these questions because I want you to understand this stuff. I don't want you to just make assumptions or guesses or whatever, it's important to understand how JavaScript works. So ES6 introduces a notion of what's called a lazy expression. This expression is not evaluated unless and until it is needed. So the correct answer here is it's not been called at all yet because it hasn't been needed yet. If I say foo of 1, how many times has bar been called? Still 0 because it still hasn't been needed yet. Okay. But as soon as I foo where it is needed, then it gets called, and every time I call foo I'm going to get it printing out that exclamation mark every time. Okay. Are you with me? Yes? And it's a new invocation each time you call foo? It is an absolute, the entire expression is reevaluated every single time. It's kind of like you wrapped that expression in a function that's not there and then you call that function. Whatever it gives back is what you put in, okay. So it's in essence a lazy expression. We don't really have lazy expressions in JavaScript, but it's sort of syntactically like that. Okay. Now you might be wondering why on earth would I want a function that would be called over and over? Why would I use this? Well, one example for where you might use it is if this one was like a unique id generation and you wanted to be able to pass in an id or have it automatically generate one if I don't pass one in. Okay. That's one usage of making a function call there. Another usage, this is kind of an interesting little trick, you could make one called required that said, for example, I'm going to go ahead and scoot this over, this one could say, could throw a JavaScript error and you could say that. So if they don't pass in the id, a JavaScript error is thrown saying hey the parameter was required. Okay. Yes? Could there be a way to refer to foo within the required function so you could name it? No. There's no way to do like a self reference or something. You could manually say like this, double that up and then say, (Typing) you could do that if you wanted to, but there's no like self reference thing going on. Okay so any expression that we want to put there we can put there and there's a variety of little usages that we can do. The point is that we don't have to do the imperative form of testing against undefined, that's automatically built in for us, we just do the declarative form, and once you become familiar with that, now that will jump out at you as being a lot more self explanatory. That's the purpose of the declarative form. Okay. Any questions about using default parameter values? Let me push your understanding just a little bit beyond what you're normally comfortable with. Okay, we've talked about scoping a little bit before, turns out that technically the spec requires that each variable, that the parameter list is a scope and each variable kind of gets its own list or whatever, so you can do self references to other parameters as long as they've already been in the list, so I could say x is equal to id. So now x is going to default to the value that id got set to, whether affirmatively or defaulted to, so it's going to go in a left to right fashion, right. So we can do a self reference. You can't go in the other order though, you can't do that before because the id doesn't exist yet, it's kind of like a let declaration in that sense, it doesn't exist yet, so now you'd be accessing the id inside of its temporal dead zone, if you will. But what about this? I'm going to see how close you're paying attention and how good you are at guessing. What do you think would happen if I said (Typing) so I've got an f that defaults to this function, which returns x. So is it returning the x from here or is it going to return that x? Alright so let's talk about these particular cases for just a moment. What's happening with the scope of the function when the function shows up in one of these lazy expression positions? That's essentially the question that we're trying to tackle here. So if I pass in no foo at all, x is going to default to 2 and f is going to default to this function, which is closing over some x. The question is, which x is it closing over? Is it closing over the x from the parameter list or the x from the outer scope? (Waiting) Okay, so most people are going to say, yep it's going to close over the parameter list. So if we try this, (Typing) we in fact get the value 2 closed over the parameter list. Well, (Typing) what do you think now? So we think it should still return 2, right? Hope so. We hope so. Well, let me give you just another moment to think. Is it going to return 2 or is it going to return 5, which x is it going to close? Five. It's going to be 5. It should be 5. Don't you just hate it when somebody asked these annoying little questions? This is the stuff that they put on JavaScript. It should be 5 because it doesn't actually get called until the console function and by then x is. No the function already closed over x, it should be 2. Alright so if we run this in Chrome, we think oh good, it's 2 that makes sense, except Chrome has a spec bug. It's supposed to be 5. No, no. Babble has this bug as well. What's happening here is that a var at the top level is taken as a duplicate declaration when there was already an x, so the x = 5 is assigning to the x parameter that the function was closed around. So that's why it's still closing over the one that you thought, but it changed, we already changed its value. It sucks doesn't it? Okay a little takeaway here, don't try to write confusing code, but I push on you with confusing code to help you understand where the edges of reasonability are, okay. I have virtually never in my experience found a case where I've put an inline function expression up in my thing, especially not with arrow functions or whatever. I don't recommend doing that, because then you're pushing on those edges of weird stuff that people have to think about. Is this stuff talked about in your ES6 chapter? Yes, yeah I cover this stuff, uh huh. Question from the chat room was could you go over that again? Yes. In this particular case, the spec says the var, and I missed this, I was confused by this and somebody had to walk me through this part of the spec to get it, the spec basically says that the var x here is not creating a new x in a new inner scope, but actually the x that already exists is part of the parameter, it's basically kind of a replacement or just a no op of that one because there's already an x in the scope of the function because that's the way it always worked from before, so they had to preserve that capability once all this new found fangled stuff that we did with parameters came along with ES6. So this var x is basically the var is a no op, so the line 4 is just x = 5, which is changing the parameter that defaulted to 2, now to the value 5 and this closure is a closure a live link, not a snapshot. So now the function returns the current value of x, which is the value 5. That's what it's supposed to do. It's the same principle of putting the unused variables in your parameter list really. Say that again. Tell me why. In the previous example, we used unused variables in your parameter list so you could assign them in the function. Yeah. Same principle is applying here. Um okay, sort of, yeah. The fact that parameters are variables that we can assign to is absolutely true, yeah. Yeah, a lot of people think that the parameter list should be treated strictly as its own scope. For the purposes of TDZ, it is its own scope, but for the purposes of the preserving the fact that they were always kind of one in the same before, this special case of var, it ends up just being a redecoration. Yeah? So what if you changed your var to let? I don't know. Does let take like precedence over var or anything like that? That would probably be an error. I think that's a static error, because you're not allowed to do let x let x in the same scopes, so I think the spec would probably throw an error there, but I don't know. The problem is we can't trust either Chrome or Babble at this point because they still have this problem. So is the parameter list x essentially a let in ES6? Yes. Okay. For most purposes, yes. Alright, so stick to the stuff that makes sense, like making a simple function call or using a simple direct primitive literal or something like that, default values will be helpful.
Gather and Spread Operators, Part 1
The next thing that we want to talk about is this notion, we might have had a function that looked like this before. We wanted to get a list, an array, of all the arguments that have been passed into foo because we're going to treat, here's a fancy word, we're going to treat foo as a variadic function. It simply means that we can pass 0, 1, 500, and maybe our function actually has different behavior depending on how many arguments we pass in, right. So in the past, the way that we wanted to get that is we knew there was an arguments object, but it was kind of annoying that that was an object that was an array-like, but not in a real array because it didn't have stuff like slice and pop on it and things like that. So let's say that there was a case where we wanted to get the arguments array and add something on to the beginning of it, we would have to turn it into a real array. So we would say something like var args is equal to, and then we did this crazy nightmare of stuff where we borrowed the slice call from a real array or from the array prototype, but called it in the context and the arguments object. That made us a real array. Okay. You've probably seen that kind of code before. Now once we have a real array, we could do something like unshift to push on the value 42 as the first argument in the list, right? And then let's say we wanted to do something like pass all of those values along to another function call, as individual arguments to another function call. So we would like to be able to just do this args thing, but unfortunately we don't want to pass the whole array, we want to pass them each as individual arguments, so we had to do apply, and we don't care about the this binding, so we have to do something stupid like null or undefined or something like that for the this, and then we give it the args array that we want to pass along. Does this code look roughly familiar? Have you see code like this before? Okay. So that's the imperative form of doing something where we pull in some arguments, add something on, pass them along. Now we want to talk about an imperative form, I mean a declarative form, okay. Um, there is a new operator added to JavaScript and its name is a little confusing because this operator does two different things, depending on the context it's used in. So it has a different name in different locations. So same operator, we're going to give it a different name. So it is also, it is now possible for us in the parameter list to gather up all of the parameters that are not, I mean all of the arguments that are not already accounted for by named parameters, to gather all of the unaccounted for arguments together into a real array, and the way we do that is with a ... operator and then we give it a name, like for example args. I'm putting spaces there just for readability, but the spaces aren't required. Okay. That does the exact same work as that whole crappy other line over there on line 2 does, it gathers all those arguments together. Okay. This is often referred to as the rest parameter or the rest operator, rather. I don't like to call it the rest operator because rest is, in this sense, a noun and not a verb, and when we look at the other usage of this operator it'll be a verb, so I like to use the verb form, so I like to call this the gather operator. Because it gathers arguments together. Okay. So now that we have an array, we can do an unshift with the value 42. It turns out we're not actually going to even need that, but we'll come back to that in just a moment. And now that we have a function call, we don't need to use apply anymore because now we can use the ... operator to spread and array out. So I can say ...args. It will spread the array out into its individual constituent parts as individual arguments to this function call. So you can see two different usages of the same operator, but they're symmetric. One is gathering them together into an array and another one is splitting the array apart, spreading the array apart out into individual values. How do we know which one is being used? Well you have to understand something about the context that it's being used in. The parameter list, though you may not have been used to thinking about this, a parameter list is what we would call an assignment context. The reason for that is that parameters are the named things in the function declaration. Arguments are the thing at the call site that we pass in. Under the covers, JavaScript does an assignment from the argument value to the parameter name. So the parameter list is called an assignment context. Exactly the same as var x = 2 is an assignment context. Got it? When a ...operator is used in an assignment context, it gathers. When it's not used in an assignment context, but rather in a list context, a value list context like this one, it spreads. Okay, so you just have to understand what context it's being used in as to whether or not to call it the gather operator or to call it the spread operator. But you see why I use gather and spread because those are nice opposites of each other, in terms of verb usage.
Gather and Spread Operators, Part 2
But here's something cool, because this is just a regular arguments list, we don't actually need to add that onto the array, we can just literally put the value 42 right there, we don't even need to modify the array. Just say 42, ...args. I think that's a whole lot more declarative form, as opposed to the imperative form over on the left. What we mean by the difference between declarative and imperative is this specifically, don't miss this, the difference between declarative and imperative is that imperative includes all of the implementation details of how, but most of those implementation details of how are not the things that the programmers should have to think about at that moment. So what we're doing is taking those details, abstracting them away into the engine inside of a feature, so that what's left is the stuff that we should focus on. How many of you have heard before that the purpose of abstraction is to hide details? Anybody heard that? That's probably how you would describe it. I don't agree. Abstraction is not about hiding things. If you go back to the original definition of when the idea of abstraction was created, what were they trying to do with abstraction? They're not trying to hide stuff, they're not trying to create black boxes. You know what the purpose of abstraction is? Focus. The purpose of abstraction is to take two things that are interwoven together and by interwoven together it makes it hard to reason about either one of them, to separate the two so that each one can be individually focused on, depending on what you need to focus on. So what we're doing is we're taking the thing that's going to happen and then how to do it and teasing those two apart with a declarative form, meaning that we can focus on the stuff that actually matters. In this case what matters is that we want to add 42 on as the first argument and pass everything else along. That's the thing that matters, all the other code over there was just a distraction to get us to that point. So the declarative form here helps us to make more reasonable, understandable, and readable code. It removes the details that distract the reading. Are you following me? Again, that's the narrative we want to be looking for. Every feature you look at in ES6, there's 1000 other features in the language that I'm not talking about today, whatever choice you use of features or whatever, I want you to evaluate it based upon that. Is it helping me to communicate more clearly? Sometimes that will mean that we create shorter code like we're doing here, but there will be times when the most communicative ES6 code is actually longer than the other one. So it's not about saving characters, it's about communicating more clearly. So you said args is a keyword here, it's a new keyword? No, no, no. The ... is an operator. Here I can call this whatever I want, I can call it foo, I mean bar, if I wanted to. It doesn't matter what I call it. Okay, thanks. I have called baz I guess. Yeah, I just, by my convention I use args, some people used to use rest because they like the rest operator usage. The ... gathers up everything that's not already accounted for, so if we had already accounted for say one of the arguments with the named parameter, ...args is going to be everything else, not including the thing that's accounted for by x. Okay. So here we would, this would have the effect of essentially throwing away whatever the first thing was that we passed in and replacing it with 42. You see how I did that? If I did the equivalent, I'm not going to waste your time, but if I did the equivalent, I'd have to do a slice and slice off the first one and then pass in a new one or whatever. It's just, it's syntactically done for us. Throwing away the parameter x that we don't care about. The question was, can we have default params with gather? So you can do defaults here, of course, you can do defaults there all you want. I think it makes complete sense that we ought to be able to default that args array to something like, for example, 1,2,3. It's not included in the language and when I ask them, can we add that? They're like, you're a bad person for asking that, so stop asking. Which is why I don't participate in the spec lists anymore because every time I come to them with an idea, they shoot me down. So I don't know why we can't have default values on these. The question was a little bit different. Was it? Sorry. They're asking if the ...args can be used as a default value? No. Sorry no. So the question was, could I say something like y = ...args? So you have to think about it, in that particular case, we are back to a value semantic, not an assignment semantic. So we're back on the value side of the equation, so ... would actually be spreading out the array. So technically you could do something like ...x, no that won't work, never mind, you can't spread out in a non-comma list. Forget I said that. So no, you can't use it like that. Sorry. Okay. I have a question. Yeah, sorry. Can you spread the keyword arguments or does it have to be an actual array? There's a proposed feature, so arguments is an object and we can't use the ... operator by default against regular objects right now. There is a proposal that is now I think stage 2, to add the ... operator, both gathering and spreading, to objects as well. It's not in the spec yet, but it's working its way through the process, so sometime over the next year or whatever we'll probably see that land. So they would be array-like objects or objects in general? I guess I don't know how that would work with like a plain object structure? Well it depends on which line of code you're talking about. If you're talking about line 1 or if you're talking about line 2? I'm talking about spread, so line 2. So if I had the ability to spread and I did this, there's no more object at all. What's getting passed along is the individual values in the arguments object. My question was, can you apply the spread operator to arguments because arguments isn't an array, it's an array-like object? Well not yet, but when this lands in a year, or however long it takes to finish, you will be able to do ... arguments. So I must have missed it, is it already approved or is it still...? It's in stage 2. Okay. There's 4 stages, actually 5 stages, and it's at stage 2. So it's working its way through. There's, I'd say, a better than 50% chance that it will land, but we don't know when. One of the complications there, just because you're asking, one of the complications there, spreading out an object is not such a big deal because you can think conceptually, oh just loop over all of its properties, but then there's questions like does that include only owned properties or what about properties that are delegated on the prototype? Should it by default give all of them? But really the gather is the question with objects. What does it mean to gather a bunch of values in? How do we figure out what the property names ought to be when we gather into an object? So there is some weirdness that they're trying to work out, but I think it's got a pretty decent chance of eventually landing.
Using the Gather and Spread Operators
There are other places where, besides parameter lists, where this gather and spread can be used. So another place is within the context of array literals. So let's say I have x is equal to array 1,2,3 and y is 4,5 and what I'd like is to define a z which includes the content, includes a value plus the contents of x, plus the contents of y, plus another value. So I would do that by saying array of 0.concat and I'd say x,y and then another array with some other value like the value 6, for example. Alright. Anybody ever used array concat before? So now z would have 0,1,2,3,4,5,6 in it, okay. So any place where I find myself using the .apply and the .slice, those are places where the ... helps, but also places where I see myself doing .concat, that's another one of those smells, like oh maybe this is a place where ... can help me. Okay. So this is the imperative form of doing that. The declarative form, if we start out again with the same array, var y = 4,5, and we have var z is equal to and now we just make an array with the value in it, we spread out x, we spread out y, and we add another value into it. So arrays can be spread out in function calls, like we did before where we were passing a value along to bar, they can also be spread out inside of object literal, I mean, inside of array literals. Alright, so we can use them inside of array literals as well, which is nice. So kind of putting those things together, I can have a foo that included this, if I wanted to pass those all along. I could put those all together and then ... it, so I could make an array that I then spread back out, or I don't even need to do that because those can all just be passed individually. All the same thing. So array gathering and array spreading can be really useful for us. And depending on how you do this, you can kind of play some interesting tricks, which I'm setting you up now for the exercise that we're about to do. You can play some interesting tricks where, for example, let's say I did this, (Typing) what I've done there is effectively throw away the first two from a, so it's kind of implicit, it's kind of declaratively slicing off the first two from a, leaving the remainder of a and the remainder of b, but all gathered together into a single array. Okay. So we can use different kinds of tricks to do the declarative form of doing all that imperative slicing and concating and all that other stuff that we might do. There's a couple questions, you should probably read them yourself. VJR is asking about the gather spread operator performance. Alright so to answer a question about performance, this is going to be super annoying to some of you probably, but I'm going to tell you that most of your intuition about how you ask about performance, you're asking an invalid question because the premise of most of those questions, including this one, the underlying premise is is it faster or slower than what I, than not doing it at all? And that's not an apples to apples comparison. What we really need to do is an apples to oranges, I mean that's an apple to oranges, what we really need to do is an apples to apples comparison, which is to ask - is it faster or slower for this ... thing to be used compared to all the slicing and concatting that I might do? That's the real question that we might ask. Now, the answer to that question is, it doesn't matter whether it's faster or not, what matters is that this is a form that the compiler can recognize because it's a declarative form, which means that the engines have the opportunity to optimize it where they don't have the opportunity to optimize the other form. That's the thing that matters. Okay. That's yet another benefit of using a declarative form. The compiler has the ability to see in the future. I don't know whether they do, but I don't care because I'm not an engine internals person. But what I do know is that the engine has the ability to recognize that syntactically and say, I know what they're doing, they're putting some stuff together and put in a super hyper optimized instruction to make that happen. They cannot do the same over here. The engine can't see what you're trying to do there up front and make a super hyper optimized form of it. Okay. So it might sound like I'm dodging the question, but what I'm really trying to get people to realize is that the value of ES6 is not about whether it is or is not more performant. In general, in the long term, every declarative form will be at least as fast as the old version and probably much faster. But whether it is today or not slightly slower or slightly faster is an unquestioned benefit in terms of code readability, and that's where I think our focus should stay. Does that make sense? Question about how this works. Um, what you have on the right side up there. So I think what's going to happen is x and y are going to be the first two elements of whichever of those arrays a and b got passed in, correct? Yeah, if we made an a and a b, like for example, a is 1,2,3 and b is 4,5,6. X is going to be 1, y is going to be 2. And if x, array a only had one element, then x would be the one element, then y would be the first element of b? Yep, exactly. So there was a response in terms of readability. Somebody trying to debate me on readability? Yeah I think so. I don't want to get into debates on it. I'm presenting you all sides and it's up to you to decide what you think about readability. Personal question, on the notion of the gather operator, what if a was a scaler? Say that again. What if a was a scaler? You can't use a ... operator against a value, unless, and here's I'm going to tell you something, but I'm not going to explain it until the end of the workshop. At the end of the workshop we're going to talk about iterators, the ... operator spreads out anything that has an iterator, which is called an iterable. If a value is iterable, it can be ... spread out. Okay. So arrays, by default, are iterables because they have an iterator built into them as of ES6. Another thing that's a default iterable is a string. If I ask, from the individual characters, if I wanted to get like a real array of the individual characters of that, the old school would have been to say something like string.split and now I'd get an array out of it. But now we can just treat that string as an iterable, because it is, and ... will actually spread it out. So if I said something like ... string, now that value is an array where each element is one of the characters from the string, so any value that has an iterator. A primitive, like the number value, would not have an iterator by default, but at the end of today we're going to do an exercise where we can add iterators to our own custom values.
Exercise 2
I think that's good on our gather and spread, so let's open up exercise 2. And this is, we will finish exercise 2 and then take our lunch break. Okay. So go ahead and open it up and I'll orient you to exercise 2 and then give you, we'll take 5 minutes on exercise 2, 4 minutes, it's a short exercise. Okay. Same kind of thing that I was just talking about before, the kinds of tricks that we can play with spreads and gathers and with parameters. The spirit of this exercise is that when I call the bar function, which is giving me back whatever comes back from foo, that's going to give me an array that if I join those together comes to this string. In other words, I'm expecting an array back with the values 2, 8, 10, and 12 in it. And the starting values that I have to play with are these two arrays. So in some way, shape, or form I want to transport those values into foo and in some way, shape, or form I want to declare foo and return something from foo that ends up resulting in an array that has 2, 8, 10, and 12 in it. Do you follow that? So using the same sort of stuff with gathers and spreads that we just talked about, see if you can declare that code.
Exercise 2 Solution
So let's talk about solution for exercise 2. We have the a1 and the a2 arrays. If we want to pass those in, we can just spread out a1 and a2. And then we have the foo function which we want to define. We want it to return something, but we're going to declare the parameters in such a way that it does the work for us. So we basically want to account for, we want to keep the first one, but then we've got two that we don't. We don't care about the value 4 or the value 6. So essentially those are ones that we're going to throw away. So we could name them y and z, or we could name them something else that indicates that we're going to ignore them. Right, we could them ignored 1 and ignored 2 if we wanted to. But then everything else, we do care about. So we can say ...rest or args or whatever you want to call it. So now we have an x value and we have the rest value and we want to put those two together. So we can say return, make ourselves an array that includes x and everything else. Now the foo name here can throw people off because what would foo be? You'd give foo some sort of a name that made sense. For example, if the whole purpose of this function is to ignore the second and third arguments, then you might call this thing ignore2 and 3, so something like that, you'd give it some sort of reasonable name that described what it was doing and now this would be a utility that, and ostensibly, this would be something that you were doing on a regular basis, so now you can use this facility to pass in a set of values and have it ignore just the 2 and the 3 and keep everything else, right. So don't get too tripped up when you see tricks like this and you see me only using foo and bar, think about it in terms of how would I use that kind of facility in my own code, what would I call that utility? that sort of thing.
Babel
I just want to take a brief moment to mention the landscape of ES6 is complicated in a sense by the fact that that syntax is not necessarily directly supported in every single browser that you may have to support. Now the good news is ES6 has been around for awhile, it was being prototyped in browsers all along during the spec process, so many of the browsers that you support already support most, if not all, of ES6, certainly most of what we're covering today. There are a few, obviously, a few corner niche cases and things, but if you have to support mobile or if you have to support older browsers, you know, and IE 11 or Safari 5, or something like that, if you have to support those sorts of browsers, the new syntax we're talking about like a ... operator, arrow function, whatever, it's not going to work and there's no real way to like gracefully handle that per se, so the typical strategy, which most of you have probably heard of, but the typical strategy is what we call transpiling. Which is to take the ES6 code that you would write and to convert it into the equivalent that would have been in ES5. So it's kind of like the side by side that I've already been doing, but doing that automatically for your code. And this is a tool that you would, this transpiler is a tool that you would run in your build process, right alongside your minifier, right alongside the bundler tool that you use, your linter, any of those other ones. Any time you do a build process, you would package up the code in this transpiled form, which means what's getting shipped out to your users is not the code that you wrote, but rather the code that's been transpiled. Now the good news is that bundle of code will work in all the browsers that you need to support because it's going to target version of JavaScript that's been around and standardized and well known for 5 to 7 years. The bad news is that there's a disconnect now between the code that's running and the code that you wrote. And whenever an error happens, an error actually happens in the code that you delivered to the browser, not the code that you authored. So for awhile now we've had a solution for that and it's called source maps. Source maps are a special kind of file that basically is loaded by the developer tools of your browser on demand or sent along with your file if you so choose, but it tells the browser, okay whenever you see an error on line 12 in character 36, that's actually line 57 character 5 in the original source code. So what the developer tool does is fetch your original code, the original authored ES6 code, and it shows it to you in the browser instead of showing you the code that actually created the error. So source maps are a way to kind of virtually map layer on top of what's really happening in your developer tools. I'd say that's probably like a 90-95% solution. Okay it's not 100% bulletproof perfect, it doesn't always catch all the different nuances and things like that, and sometimes your transpiler does stuff that you don't even realize that's it's doing. Sometimes there are tricks that happen and tricks that they play that you might be debugging it thinking a certain set of code ran and didn't. So I just wanted to quickly show you one of the tools that's most common, I'd say probably 98% of people that do transpiling use a tool called Babel. Now this is an online form of their tool, but they have obviously they have a command line version of the tool. So I just want to quickly illustrate with some of the code that we've already been talking about and you can see, as I type the ES6 version of it on the left, you'll see on the right what the ES5 equivalent that we're getting. So imagine if we wrote something like function foo that takes an x with a default value and it also takes a ...args and all of the sudden you see it start to write some other code that we haven't had to write. And you see that that code is roughly equivalent to the kind of code that we were already talking about. So you kind of see that it's already happening. So now let's say we pop, we call a bar function and we're going to pop in another value and then we're going to spread out the args and then we're going to pop in another value and pass those along to a bar. Now a bar doesn't exist yet, but all we have to do is define one and now you see, again, all of that extra logic that really needs to happen and make sure that stuff is getting copied over and sort of bullet proof spec fully spec compliant stuff that you would have to be writing yourself. And the reason is how you this is, again, to make the illustrative point. Both of these pieces of code accomplish the same goal. In a browser that can read ES6 like my Chrome can, I could deliver this code or I could deliver this code. But it's not so much about the delivery that we want to care about, although the delivery is the practical thing, like how do I support browsers? The real question is, haven't we been writing code like this for the last 21 years of JavaScript's history? And hasn't that code been failing to properly communicate because it's so cluttered up with so many details? It's so intertwined, it's so complected with implementation details that the actual code underneath the covers, the stuff that we really want to care about, the stuff that's over here on the left, we can't even see that stuff because it's all mixed up, jumbled up together with these other things. That's the value of writing ES6, it's not about shiny new toys, it's not about saving characters, it's about making code that gets out of the way all the stuff that doesn't matter so we can focus on the stuff that does. Okay. One little note of caution here when you look at a tool like Babel, there will be times where you type in a piece of code into Babel and then you look at the transpiled output, now I fully recommend that you ought to do that, you ought to go and look and understand things or whatever, but take it as a guide not as a spec compliant 1 to 1 mapping. Because what Babel is doing is a very sophisticated set of analysis on top of your code and they're taking the shortest path to getting just your code to operate correctly in an older environment. They're not necessarily adding in all of the spec compliant details because if they can figure out that you don't need a spec compliant detail, they leave it out. So they do all kinds of analysis to take the shortest path and do optimizations and tree shaking to eliminate dead code, all kinds of crazy things that they do, in their outputted code that is not necessarily the same equivalent as what you might write if you were trying to write spec compliant equivalents of your own ES6 code. Now I regularly get people, you know, once or twice a month, I'll get somebody that will email me or Tweet me and they'll be like - hey you said this thing in one of your workshops where it does this and Babel says different, ha, ha you must be wrong. The fact of the matter is that Babel is not tasked with producing compliant code from a spec perspective. Babel is tasked with producing code that behaves correctly and as performantly as possible. That's what we would want out of a tool. We wouldn't want a tool that produced crappy tool. We'd want it to produce the best possible code that it could. So while it's informative to look at those differences, don't necessarily take that as your official education on how ES6 equivalents work because they're doing a lot of analysis that you normally wouldn't do and a lot of nuance that we don't even cover in our discussions. Does that make senses? Alright, so that's Babel. I fully recommend that you look into that tool, start using that because what I'm teaching you about ES6, I want you to start using today. I want you to go back and there's low hanging fruit, simple stuff like dropping in the ... operator and dropping in a default value here and there. You don't have to completely remake your entire code base from scratch with arrow functions and proxies and all of that other crazy stuff to get value out of ES6. You can take small little parts and convert one idiom at a time, one declarative form at a time.
Destructuring
Audience Q&A: TypeScript vs. JavaScript
So let's go back to our discussion. Again we're going to keep in this form of looking at the, or this pattern of looking at the declarative form versus the imperative form. First we'll look at an imperative form and then a declarative form. The next feature we want to turn out attention is, I think without question the most complex, and by complex I mean lots of steps to learn, of all the JavaScript features out there and certainly of all the ES6 features. Okay. And I'm fully aware that when I present something to you that has a high level of learning, that the first 30 minutes that I'm talking about this topic it's going to be a little bit confusing and that is totally okay. So don't feel like you're somehow behind or whatever if this is weird or confusing, it took me months to get comfortable with this stuff, okay. And I'm also fully aware that if I tell you something that takes an awful lot of learning, that might seem to kind of betray the whole purpose of what we were trying to get here, which was to try to make it easy for people to not have to learn a whole bunch of stuff to understand our code. So that's another nuance, if you will, that I want to make sure we're clear on. Don't ever hear me to say I don't want somebody to have to learn something. Right, that is the antithesis of my message. You're here today and you're in this workshop, you're watching this workshop because I hope that you have the same spirit as I do, which is my challenge is for you to learn deeper, to go deeper, uncommonly so, more than most of your peers do. And so I want that and I want to promote that, so don't ever hear me to say I don't want you to have to learn something, that's not the point of creating readable, understandable, familiar code. But there is a difference between learning something that pays off only for that line of code and learning something that pays off for every line of code like it throughout your code base, and that's the difference I want to draw here. There are all kinds of tricks, as a matter of fact, I've thought in the past about maybe writing a book, and maybe someday I will just for the fun of it, maybe writing a book about all the crazy esoteric tricks that JavaScript programmers can pull, because there's all kinds of tricks that we can play in our code. It's a very sophisticated language when you start really poking at all the edge cases, and there's a lot of cool stuff that we can do, and that's fun, right. I'll be the first to admit, it's fun when you can pull some kind of trick out of hat and like, look how this thing just fits together with that and they just work right, or whatever. You know, one example, just to give you an example, I love the fact that function.prototype is itself an empty no op function. So instead of somewhere in my code over and over and over again that or the arrow equivalent of it, I like the little trick that I can just use function.prototype in those places because I know it's just a plain old, simple, empty no op function and in places where I want that, it's a nice useful thing. So there's, that's just one example of a hundred. Right, just little tricks that we can know about the JavaScript language. But the fact of the matter is that these tricks can be divided into this spectrum where there are some tricks on this side of the spectrum that if you learn them, they're really only going to pay off in that one spot. Put a lot effort into understanding how this one statement works, but you're never going to see that ever again throughout the entire code base. Okay, so I put a lot of learning in and I got one line of code read. But there are other tricks, there are other techniques, there are other patterns that we can use as developers, that pay off in folds, they pay off bigger than themselves and in more places in our code than in just one spot. So wherever possible, what I want you to do is focus your attention on using and learning the things that pay off in a bigger sense, as opposed to look how clever I can be in just this one little line of code. Look how I can use the double tilde operator in this really weird way and I've saved myself a couple of characters, but there'll never be another case in my entire career where I see double tilde so my time spent to learn that doesn't pay off very much. Does that make senses? Are you following me? This is a balance, there's a balancing act in play. Yes? A question from the chat room. What are your thoughts on TypeScript? Similar to Babel? Different? Well, similar to Babel in the sense that they both end up processing your code and producing a different version of your code that runs. Different that TypeScripts job is to take features that aren't part of the spec and make them work in a spec compliant browser, whereas Babel's job is to take stuff that's either part of the spec or hopefully maybe going to be part of the spec and make it work in the browser. So there's a difference there. In general, I mean I often get asked this, I often get asked what do you think about TypeScript? I also used to get asked what do you think about CoffeeScript? And my answer is kind of the same for both of them, which is eh, I don't use them, but I don't have any problem with them. As a matter of fact, I think it's a good thing that CoffeeScript existed. Many of the great things that we're seeing in ES6 are here in large part, large part, because CoffeeScript first pioneered whether or not it could even work in the JavaScript ecosystem. I think the good parts of CoffeeScript made it into ES6, for the most part, right. So I think it's a great thing that CoffeeScript existed. I wouldn't ever write a line of CoffeeScript again, I'm pretty sure CoffeeScript is like DOA at this point, right, like it's a dead horse. So I wouldn't ever recommend somebody write CoffeeScript today, but I'm glad it existed, and I think we need more of those. I think we 1000 more experiments into language design so that we can practice with this stuff before we get it set in stone in our language and we can never change it. Because that's one thing, as soon as we make a mistake, we're stuck with it forever. So I think it's a great thing that we have explorations of that. And I think TypeScript is in that same vein, it's an exploration into A) what could classes look like before classes were standardized, that's one reason people use TypeScript, because they were the first ones to kind of pioneer what we now know as the ES6 class syntax. I'm not a fan of that syntax, but if you are a fan of that syntax, TypeScript is a great place to have been working with that for quite a long time. And the other thing, of course, is that TypeScript is designed to add the type annotations thing and so you have a compiler enforced set of behaviors around what your types ought to be. Types have never been the source of bugs in my program, so TypeScript isn't the tool that I reach for because I don't have that problem. But there are certain places where that problem is absolutely valid and if you're writing, for example, all day long if you're switching back and forth between JavaScript and .NET, you probably ought to be using TypeScript because it just makes a lot more sense, to keep in the same mental context. It's not my world but I think it's a good thing that that exists for those sorts of use cases. So hopefully that answers the opinion. Okay, so let's now turn our attention to this destructuring thing. And stick with me because it will be a little bit confusing, but I believe that if you give this the weeks of time and practice, if you look for opportunities to use these techniques, I think it will pay off beyond just those lines of code.
Array Destructuring
Let's set this up real simply, let's imagine we have a function called foo that returns us something and I'm going to start easy, I'm going to start with an array. Let's say it returns us an array like 1,2,3. And then we want to consume that value in some other part of our program. So we're going to call foo and what we're going to get back is an array, which is great, we can say var array and get back that array, but what if in that part of our code we need to individually reference different values. We don't want to say arr of 0, arr of 1, what if we want lexical names to reference them, like for example a, b, and c? So what you might be in the habit of doing is something like assigning this the tmp variable, and then creating individual assignments. Whether you knew it or not, this practice has a sort of general category name, and that practice is destructuring. And that term, that idea of manual destructuring, the way to think about what that means is to take a structure, like an object or an array, and to deconstruct it down into its constituent parts through assignment. Okay. So really destructuring is an assignment operation. It's taking some bigger thing and assigning off its individual parts into different variables. Okay. We can do so manually. But this, of course, is the imperative version. And it's not too complicated right now because there's only three of them. But we can start to throw more and more bits into the process and very quickly that imperative code is going to get super complicated to read through and understand. So now we want to look to does JavaScript give us an option? And the answer is yes, it does give us an option, a declarative form of this destructuring assignment. Okay. So same code, we'll start with the foo. But now what we're going to talk about is what's called array destructuring. This is going to look strange, okay. It's going to look strange if you're in the habit of trying to think syntactically about what you're writing, this is going to be something new that you haven't seen in JavaScript before. What we're going to do is, and I won't even put this part yet, I'll put on the other side, we're going to take the foo value, the return value from the foo call, and we're going to destructure it. The way we're going to destructure it is we're going to declare over here something that looks like an array, but it's not an array. And that array looking thing, that's not actually an array, is going to say where we'd like stuff to be assigned, it's basically going to list out a set of targets for those assignments. So we could say something like a, b, and c. So it looks like an array on the left hand side of an assignment. If it were an array, this would be a nonsense statement because there's no such thing as having an array be the source of an assignment. An array is a value, it is the, I'm sorry, there's no such thing as making an array the target of an assignment. It is always the source of an assignment because it's value But this isn't an array, what this is is a pattern. Okay. There is an analog that's similar, not the same, but similar in other programming languages that you may have heard of before called pattern matcher. This is not real pattern matching, but it's kind of in the same vicinity, because what we're saying to JavaScript is I'm going to declare a pattern that describes what sort of value I'm expecting here and I want you to take that pattern and do the work for me. Okay. So what we're doing is saying, we are expecting an array that has at least three values here and we would like for you, JavaScript engine, to take that value, deconstruct it down to its individual parts and take the item in the first position, assign it to a variable called a, the item in the second position to a variable called b, and the item in the third position to a variable called c. It's a declaration of a pattern for assignment, rather than telling JavaScript how to do the assignment, we're saying this is the end result that we want. Are you following me? Now it won't be obvious yet why, but it will as we go along, that I'm going to say as soon as you have more than like two or three of these, you're not going to want to put these all on one line. Just like it's not a good idea to try to put your arrow functions all on one line just so you look neat. I think you're going to want to spread these out, okay. Now something about JavaScript developers that I know is that we are absolutely allergic to white space and new lines. We do everything that we possibly can to get rid of this unnecessary white space and new lines and it's absolutely ridiculous to me because white space and new lines are free. And they're one of the best tools that we can use for readability. So I'm going to say, stop that habit of yours, of trying to look for every possible way to like get stuff up onto another line, use new lines and white space indentation liberally because it's one of the best ways that you can make your code more effective at communication. Look for the opportunities that you have to format your code in such a way that it will more readable. Okay. So what I've just done is broken those out onto another line as opposed to putting them all on one line. It won't be terribly obvious yet why I did that, but you'll see in just a moment why I really think you're going to want to get in the habit of putting your destructuring patterns broken out across multiple lines. Breaking it out across multiple lines also has that side effect if you use something like get, of making your difs more clear. True. When I've added one value. Very true, that's the, yeah that's not really anything about destructuring, but you're absolutely true. If you break things onto multiple lines and get obviously makes its differences in a line based sort of way, so it's a lot easier when you have multiple lines to know the differences are. Of course, now we have the problem of comma first or comma last with gets, or whatever, but that's a separate issue, right. Okay, oh and by the way, you can do trailing commas, so you can kind of get a little bit away from the get problems if you want to. Okay, so what we've said here is hey go break this down and assign these things. Now what would happen if the array that we got back actually had fewer values? Well just think about it from a processing perspective, what would happen is exactly what you would expect, which is it would try to go to the array value and it would look for an item in position 2 because we said we were expecting an item in position 2. What would it get back if it said array of 2? It would get back undefined, right? Because that's not a present value in our array. So c is still going to work, the assignment is just going to be that c starts out as undefined. Okay. So it's totally okay for our pattern to account for more values than we actually get. It's also okay to get back more values than we account for. If we only account for these three, the value 4 that comes back gets silently thrown away, no big deal. So this pattern does not have to be a fully inclusive pattern. It can be a partial pattern for the stuff that we care about. That's the point I'm trying to make, okay. Yes? Can you have default values in the? We're getting there, we're getting there, okay? This pattern says I'm expecting something kind of in this shape. Let me give you a scenario, it's not going to look obvious yet, but let me give you a scenario where I really think this starts to shine. You call a function or get some value back from say an API call to some JSON API, and you get back some big complex object of arrays of objects and all of that other stuff and you want to reference things three or four levels deep, down inside of this one part of this big object that you know you're getting back, you want to reference those with lexical variables. What do you do? You end up doing some really complicated version of what we have on the left where is says tmp of 0.a of 3.foo.bar.baz of 1, going really, really deep and all this crazy nesting into it and now your code is super imperative and super confusing. Or, you can declare a nice clean pattern that says this is the shape of stuff I'm expecting to get back and if I get something that matches the shape, I want you to make these assignments. That's the difference between declarative and imperative here.
Destructuring and Default Values
Now it might be the case that I would like, in the case where, for example, I don't get anything back for b, I might like to apply a default value. If I were doing that over here, what would I end up doing? I would end up saying something like tmp of 1 not equal to undefined, tmp of 1 or and then give it a default value like 42, right. So now imagine all of that craziness added in with all that undefined checking stuff that we'd be doing, and you can probably quickly imagine how crazy hairy that does because you've also probably written code like that before, I certainly have. Because I've needed to this kind of stuff on more than one occasion, many occasions. Okay. Here's how simple it is with destructuring. We simply say the default value for b should be 42. And that default assignment is going to behave exactly the same as the parameter defaults that we already went over. It has all the same things, it's a lazy expression, you can do stuff in that just like you can elsewhere, including the required call. All the same things that we already learned, it now applies here. Okay so now we're declaring that b ought to be defaulted to 42. In the case where b comes back as undefined, we get 42, if it comes back as an empty slot we also get 42. Okay. So we can declare our default values. Now the question that we want to pose now is - what would happen if I did that? Or if I did return undefined or I had no return at all? What do you think would happen? (Waiting) They'll be undefined. So if a null value or an undefined value came back over here, always think about it in terms of the processing model, if tmp was null or undefined over here and then we tried to say tmp of 0, what would happen? Reference error. We'd get a JavaScript error, it's not a reference error, it's called a type error because we'd be trying to access a property, a 0 property in position in the null or undefined value and that's not allowed, so we'd get an illegal operation, right. So the same is going to be true here. If you have a pattern that's expecting an array and you don't get back an array or at least something that can be accessed like an array, you're going to get an error. So one way to guard against that is to say foo or an empty array, because if I got myself an empty array back, then it would be totally okay for me to do a pattern, an array destructuring against an empty array, I'd just end up with a undefined, b 42, and c undefined. You follow me? Now all the same caveats apply with this or here, that if we are expecting to be able to get back, you know, return back something like a false or a null or an undefined, this isn't the appropriate one, but in this particular case, in destructuring, we've already said we are expecting an array, which is always truthy. So this pattern is pretty safe. It's not, we're not likely to run into any scenario where the or is going to bite us and create a problem for us. It's pretty safe because we're already saying we expect an array. If you create a function that's supposed to return false, you should expect for the pattern to fail because that's not a valid case. So the or empty array here is a way of saying, hey go ahead and just default to the empty array if that occurs. Right. Alright now what about this, what happens if I have an array with a whole bunch of values in it? And maybe I want to account for the first three and I want to gather the rest of them up into an array, ...args, just exactly like we did in the parameter list. Why is this a gather versus a spread? (Waiting) Are we spreading it? Groups. Because we're in an assignment context, aren't we? We're on the left hand side of an equals. That's what we mean by assignment context, as opposed to a value context on the right hand side. We're in an assignment context, exactly like we are in a parameter list on the left hand side of an equals. So a ... used on the left hand side of an equals, anywhere on the left hand side of an equals, is going to be treated as a gather, as a rest, as opposed to a spread. Okay. So here we're saying gather up all the rest of those values into an array called args. If we don't have any, args is simply an empty array. Okay, exactly the same as it would be in a parameter scenario. (Waiting) Now I've used this with a var keyword just because it's kind of like easy and convenient, but it turns out that the structuring is really just assignment, it's got actually almost nothing to do with declaration. It's kind of a shorthand that we kind of streamline in the declaration, but if I already had an a, a b, a c and an args declared somewhere, I could do this assignment all by itself. It doesn't have to be paired with declaration. Okay, so really the only constraint is that these things need to be things that would be a valid left hand side of an assignment, a valid what we call assignment target. So what about this, what if I had an o object, and I said I want to put all of these as properties in an object? Every one of those is valid assignment targets, so it's totally okay to parameter, to array destructure into a set of objet properties if we wanted. They just need to be valid things that could show up on the left hand side of an assignment. (Waiting) Questions so far? (Typing) Are arrays the only things that this pattern matching works? Nope we also have object destructuring, which we'll get to in just a moment.
Dumping Variables
So if we know that we can receive assignments without declaration, we can also do an interesting little trick, this is just a little side note, it's not something I do an awful lot, but you remember earlier when we were talking about using block scoping to do that whole like swapping thing? Remember we did left tmp = x, x = y, y = tmp, remember that? We can use array destructuring to do the swapping. Let's say I had an x that was 10 and a y that was 20 and I wanted to swap the two. I could do that whole block thing that I just showed or what I could do is say, an array destructuring with x and y as my pattern should be destructured from an array that has them in the opposite order. Of course that's not limited to two, you could do as many of those as you wanted to, right. While we're on the topic of using this array destructuring stuff, if we had an a that was an array like that, we could say, for example, (Typing) so now we're making an array, a composite array, I'll use spaces just to make this a little more readable, we now have an array on the right hand side that has 0 through 4 in it, right. And over here would say I want to throw away the first two of these, we're going to have to make variables to catch those, collect the rest of them right back into a. So that is the effect of throwing away 0 and 1 and now a is going to have 2, 3, 4 in it. Okay. Remember our whole usage of ... operator, it also works with our destructuring. So now we see the ... on both sides, one on the right hand side it's doing spreading, on the left hand side it's doing gathering. And we could just have a single variable here, like that or underscore or whatever other one you wanted to use to say this is just, I'm just dumping these, I don't care about them. Okay. But you don't even need that because array destructuring allows you to do empty slots. So now if we ran this example, our a would be an array that had values 2, 3, and 4 in it, 0 and 1 would have been dumped. Okay. So we can use our pattern destructuring in even more, I don't necessarily want to call it a tricky way, but we can use the capabilities of it to take on tasks that would have been a lot more ugly to express in the imperative form. So I take it if you transpiled that it would just give it some random variable names that never get used again on those two that get tossed out? Oh it just slices instead? See they're doing all this analysis to figure what's the simplest path, fewest characters, most performant to get it. So you might say, oh this is how I would do it, I'd make these temporary variables or whatever, and they're saying, we've already analyzed it and figured out this is the best way to do it. So with the empty slots, does the white space matter? Or is it just the commas? Yeah, I do that for readability, it doesn't matter at all. (Waiting) (Typing) There's the array with 2,3,4 in it.
Nested Array Destructuring
If we can destructure a single level of arrays, we can obviously destructure nested arrays. So I could, it might be the case that I would have in this position an array that had 4,5,6 in a nested array, okay. I'll spread that out so it's a little easier to read. Okay. So now at the fourth position, we actually have an array with 4,5,6 in it. What is args going to have in this particular case? Tell me what the structure of args would be? The array 4,5,6? No. (Waiting) An array where the first item is the array 4,5,6. Exactly. Args would be an array with one item in it, the item in that one position in args would be the array 4,5,6. Okay. Do you follow that? Alright but what if I didn't want that? What if I didn't want that thing as one big array? What if I wanted 4, 5, and 6 individually addressable? Well I can do array destructuring right inside of my pattern, a nested array destructuring that says make d the first one, I don't care about the second one, and make e the third one, for example. As long as I have those as locations that I could assign to. So now d is going to be the value 4 and e is going to be the value 6. Array destructuring uses position, just like regular arrays use position. When we talk about object destructuring, you'll see those use property names to do the mapping, but here there's an implicit mapping from 0 to a and from 1 to b because we're using the position instead of the property name. (Waiting) So now you can start to see, you can start to visualize that if I had a really complex structure coming back from an API, I could have a nice little nested list of all the places, of all the stuff that I cared about, nesting down an array, inside of an object, inside of an array, inside of whatever, and then I'd have my two or three little variables that I cared about, like first name and email address, and those are the only ones I care about, and the pattern becomes a self documentation. How many of you have ever done before, have some kind of API that you're consuming and what you do is take a capture of that API structure and put it into your JSDoc comment? Because you need to document what the hell you're expecting to come back from this function call, right. Well now your pattern becomes your self document. You document what you're expected by doing the destructuring pattern according to what you're expecting to get back. No need to duplicate it in a JSDoc comment, it's just right there in the structure. Okay. That's what I meant when I said earlier that destructuring becomes a thing that pays off bigger than just itself because now it starts to teach you something about the bigger aspect of what's going on in the code. The time spent for you to learn how this works pays off far beyond just that one line of code. Question about array and object? Yep we're going to get to mixing them in just a little bit. So can you talk me through the, a moment ago on line 11 you had ...args, Yep. and I don't think I ever got how that would come back as a single element within an array. Can you talk through that one again? Yep, let's go through this one one more time. ...args is at the top level of our array destructuring pattern, which is saying any items that are left in my array should all be collected up into an array called args. What is the only item that's left in our array? It's an array value? So there's going to be a single array holding that one value at position 0. Does that help? Oscar has a question about why do you need all those vars at line 5? Only because I'm not putting the var right here. Remember, I switched from doing declaration syntax to doing general assignment syntax. So if I go back to the declaration syntax, it's a little easier for us. Okay. Not that I would encourage doing it, but does it work if you don't have a var and you treat those as globals? Or will it not allow global assignments? No, it will allow global assignments, I mean if you don't have a var there, it'll allow a global assignment, but then you have the question of if you're in strict mode, which you should be in strict mode, then you're going to get an error because that's not allowed in strict mode. (Waiting) Okay, so we can either account for one of the values here or we can destructure it, or we can collect it with a ...args. But we can't do both. That will be different when we get to objects, it will be possible to double account for something with objects, and we'll get to that in a moment. But with arrays, this position is either ...args or it's a variable called d or it's a destructuring pattern that's broken down further. You've got to pick one of those three. Okay. (Waiting) Alright another thing, before we move to talking about objects, is this idea that, make sure I have the best way to illustrate this. (Typing) What's happening here, if we went back to that simple case, is we do have a declaration that's occurring, but we also have, and actually this is going to be a lot simpler if I do it this way, so I'm going to go back to that case where I'm just doing destructuring assignment without declaration, just keep this simple. What's happening here is that there's an assignment expression happening and the end result of the assignment expression is itself a value. We already saw that earlier in one of our exercises where I assigned an arrow function to a variable and then the arrow function came back from the assignment. So here we have, what do you think would be the value of x? First off, do you think this is valid syntax? Yes or no? And by that I mean, convince yourself that it is valid syntax. Why is it valid syntax? Why is this, for example, not an array? Because it's still an assignment target. Because it's still on the left hand side of that assignment. So it's still in an assignment context. Now this assignment happens and some value comes back. Many people think that x will be the array of 1 and 2. Because they're thinking to themselves what comes back from that assignment is only what I captured in my destructuring, but that is the wrong thinking because that is thinking that bracket a, b is an array. Bracket a, b isn't an array, is it? It's a pattern for how to break down an array. So what do you think actually comes back from a destructuring assignment? The whole array, right. So x is actually going to be the whole thing, 1, 2, 3 with an array and 4, 5, 6, that's what comes back from the assignment. Okay. The destructuring assignment is not creating a new sub-array of just two items, it's breaking down the array according to a pattern. That's a subtle, but really important processing signal. So in the same vein, you could switch x with the a, b destructuring and you'd still get the same result, right? Because x would be the entirety of foo. If you did that, now your declarator would apply to var a and b and this line would be redundant, but x wouldn't exist, so you would need to declare x. But otherwise, same result, correct? Same result. Okay. But I want to go back to the case where we don't have the declarator involved, I want to go back to this particular case because I just said, and that's what prompted me to go off on this little rabbit trail, I just said, oh well you can either account for a position using a lexical name or using a ... gatherer, or using a nested destructure, but you can't do multiples. Actually, you kind of can because you can chain multiple destructuring patterns together since every destructuring assignment returns the original full array. So I could say something like a, b ...args, which is going to collect 3 and the array 4,5,6 in, and I could also have another destructuring. I'm going to start breaking these onto multiple lines for readability. Okay I could also have another destructuring which doesn't care about any of those first ones, but starts breaking down 4,5,6. Okay. Let me try to use some white space here to help make this a little bit more readable. (Typing) So let's go from the bottom to the top, first the foo function returns us an array. We destructure assign a to the value 1, b to the value 2, and collect into args the value 3 and the value 4,5,6. You with me? Then we say the whole array again is returned and then we do another destructuring, which throws away the first three and then it goes into that 4,5,6 and it says c should be the value 4 and d should be the value 5. So it is possible to chain multiple destructuring patterns together to accomplish that trick of doing accounting for the array in multiple ways. Any questions about array destructuring? When you do multiple levels like this, do you have to have the, I'm assuming you need var a, b outside of the? Yeah, we would have had to do all our _____. The var is only going to apply to the left most pattern, so we could have done var here and then had a var a,b and args, or we could have done all of them, which is probably what I would have done, and had no declarator attached. Yes? So a request for more concrete function and var names. I know why you do this, but I thought maybe you'd want to. I understand the request for more concrete names. I don't really want to get into the full reason why, but I just want to tell it's not because I'm uncreative or lazy, there's a reason why I reach with the foo and the var and there's a reason why I try to make your exercises not necessarily use too much of the foo and the var. So we're doing both and teaching the concepts with the simple stuff and then we try to apply it with less weird stuff. That's not always the case, a few of the exercises have to use those, but. What about args? Is that, do you still think of using that here? Because to me it's like args might be used in a function. Yeah, maybe there's a better name for that. In that one I would just, you know, could you call it some letter? Whatever. Or vals, maybe that's a better one. Yeah I was just stuck in the mindset. Now I'm happier. Okay. In particular, when we get to the end of this discussion of destructuring, which we've still got a little ways to go, but when we get to the end of the destructuring, the exercise that you do on destructuring is a more concrete example, it's not a foo bar example. Okay.
Object Destructuring
So let's reset back to the case with objects and a lot of this is going to start to be more self explanatory now because you already know what to expect because we've seen it with arrays. A lot of the same things are going to be true. We could have done this as tmp.a and tmp.b and tmp.c. Okay, that would have been the pre-ES6 equivalent. Now what we can do is take this function and we can do an object destructuring, so I'm going to do an object pattern with curly braces instead of brackets. And in my object pattern I'm going to say, I want the a property to be targeted to an a assignment location. Okay. And I want b to go to b and I want c to go to c. In the case where our target and our source are the same, meaning we don't need to do any alias or renaming, we do not have to specify them twice. We can just simply say a, b, c. It will assume that means go get the property of name a and assign it to a lexical variable called a. Okay. But if we wanted to, for example, reassign the b property instead to a variable called capital X, we now have declared an a variable, an x variable, and a c variable, but not a b variable. Are you with me? Oh, so the X is actually the target, Correct. not the source. Correct. Interesting. Yep. The way to remember this is that with object literals, the property name is always on the left, with object destructuring the property name is always on the left. These can, of course, also have default value assignments. So the equivalent from over there, we just add on the equals, right. With or without the colon, we can add on the equals. (Waiting) Same thing applies here, if we return something that was not an object, could have a failure here. So our little guard is to put an empty object to be destructured in the case where something falsey came back. Of course if our pattern doesn't account for a property, no big deal, that property just doesn't get worried about. If our pattern accounts for a property that doesn't exist, we end up getting a variable that's just undefined because we accessed a d property on an object and it didn't have one. Question in the chat is - shouldn't X and b be switched in order? No, this is how it works. Property name is always on the left, that's the easiest way. We can get it in nuances of grammar and there's ways to twist your brain into thinking it's backwards, I have found from my teaching experience and from my personal dev experience, the way to keep this straight in your mind is to think whenever I make an object literal, the object property is always on the left and then there's a colon. When I make an object destructuring, the property name is always on the left. Now property name and value have, or target and source, have different meanings, those get flipped semantically between the two contents. But the thing that stays the same and the easiest way to keep this straight in your brain, proper name is always on the left. That makes sense once you think about the fact that that's, you know, at least in this case, you have the var there and that's your target, because that's why I asked at first because I was like, well how can you have a, b, c, on the right hand side when those aren't like existing properties, and the answer was you don't, you're creating those. Yep. Otherwise I would have expected it to be like the string a or something to grab that property. Yeah. So if I wanted to use that destructured object, I would just give it a name and then I could use it for whatever I need it for. You mean, you wanted to capture the object and also destructure it? Let's say I want to destructure the return value from foo and then store that in a variable to use later. Okay that sentence doesn't make sense. Once it's destructured, it's no longer an object, it's a bunch of individual assignments. You could capture the actual object that came back from foo and then also destructure it, but you can't do it the other way around. So if you wanted to do the former of those, which is to capture a reference to it, you could have an obj somewhere and you could say... So now obj is going to be made a reference to the object and then that object reference comes back and gets destructured. So now we have both the object and the destructuring assignments from it. Is this similar to the array destructuring where you could technically switch those? Like, does the object destructuring also return the full object? Yes they do. Okay. What's also true is that if you switched them, your var is going to apply to the first one and not the second one, but yeah. Destructuring both arrays and objects always returns the full thing that's being destructured. I don't suppose there's anything built in right now that's similar to a rest for the arrays where you could get all the other properties of the object? That's what we're talking about when we say there's a stage 2 proposal for object spread and object gather, this would be where you'd want an object gather, but then what does exactly that mean? There's a lot of weirdness, especially when we start thinking about prototypes chains and stuff like that. What if I gathered into an object that already has a read-only property higher up in the, there's all kinds of craziness about it, but they're working through that spec process to figure out whether or not it's something they can add, and I think there's a good chance it lands at some point. Alright I'm going to take off the object one just so we have a little less noise here, but.
Nested Object Destructuring
Of course if few have an object at one of those positions, like for example up here, if in the d position it is its own object and that has an e property on it with a value 4, for example, we can do nested object destructuring, just like we can do nested array destructuring. So here instead of putting just d by itself, I'm going to need to do the d property colon, but instead of having a lexical variable for it to be targeted at, think about this processing wise, what I now need is a destructuring pattern for it to be targeted at. So I'm going to put my destructuring pattern there. So now here's my destructuring pattern, now I have a e variable that is the value 4. So in this particular set up, could you also have an additional assignment of d to d? Yep absolutely. We hadn't gotten to that, bust since you asked, can't really straightforwardly do that with arrays, but with objects we can account for d a whole bunch of different times if we wanted to. There we've assigned off whatever was in d, which is an object, into variables X and Z, and yes that overwrote the b because it's going in the top down, and we've also destructured the contents of d's e property into an e variable. So we can multiply account for a property. Yes? From Anthony, if you set in a pattern a default value for a property of an object, but that property does not exist for the object, will you still get undefined or will you get the default value you set? Ah, so it's exactly like with parameters. The question is, does the absence of an object property represent the undefined condition? And the answer is absolutely yes. As long as that property doesn't have a prototype or some proxy or something like that, yes. The absence of a property ends up behaving like it got back undefined. Just like if you said, o.abc of some random object, you'd get back undefined. Same thing here. If undefined comes back, the default condition kicks in and gives you that value instead. Okay. Alright now what if this d object, which we're doing a destructuring, what if it didn't exist? What would happen? We would be trying to destructure an undefined value, which is an exception case, right? So guess what we need to do here? Provide a default. Okay. Similar to how we provided a default here using the or operator because that's JavaScript, in destructuring syntax we need to provide a default empty object to be destructured, in the case where d doesn't come back. So that default is used as the source of the destructuring then, right? Not the out? The value, yes. The value to be destructured. So going back for just a moment in this particular case where we have a pattern that looks like this, it comes from some value somewhere, the processing order is a little strange here, okay. And this is part of what takes a little bit of getting used to because what's going to happen first is it's going to go and look and see if there is an a property, so we're starting here on the left, it's going to say is there an a property in the object that we're expecting? If the answer is no, it's then going to go all the way over here to the right and say, okay let me go ahead and get the default value to use for that in place of that property, then it's going to come back and say, what is my target? In this case my target is a regular lexical variable, but my target could have been another pattern. So it's going to go from the left to the right and then back into the middle, so that is a processing order that seems a little awkward in those cases, but if you think about it in terms of the imperative code that you would have written, it makes more sense, because you would have written it in that order. You would have said, go look and see if it's there, if not apply the value and then assign it somewhere or destructure it somewhere. Okay. So you have to get used to doing that left, right, middle sort of jump, it's not strictly left to right here. If we can do objects inside of objects and arrays inside of arrays, of course we can do objects inside of arrays and arrays inside of objects. So this could have been an array destructuring, a nested array destructuring where we're expecting an array, but what would our default value should be here? It should be an empty array instead of an empty object. Okay. Now I don't know for sure why they didn't make this required because I'll tell you that 99.99% of the time I want it and 98.99% of the time I forget it. I've been doing this awhile now and every single time I write it I forget it, I've already forgotten it twice today when I intended to put it in our earlier discussion of arrays. Okay. I forget it all the time and I wish it was like a grammatically required thing. I can make a guess as to why it's not grammatically required, and the guess is it might be the case that you want a hard failure if something's missing. If you want a hard failure, you leave off the default value and you'll get a hard failure. Most of the time I don't want hard failures. Most of the time I want it to kind of like gracefully recover or figure out, okay just destructure an empty array and leave me with my default values, don't bother me with that, okay. So most of the time I prefer that soft fallback sort of behavior, but it might be the case that you run into an occasion where you want the hard failure, you want to catch it with a try catch or whatever. Okay. So that's why you are allowed to leave it off. Here's my opinion, you ought to get a linter rule that forces you to put it in every time, and there are linter rules written that force you to put in the default assignment for your destructuring, for your nested destructuring specifically. (Waiting) One last little detail about the object destructuring before we start talking about maybe some more practical usages of it. One last little detail is that your object can also be just a general assignment destructuring. So if I already had all those already created, like var a, b, X, c, e, if I already had all those already created, then I could just do a general object destructuring. However, unfortunately this is not legal syntax to start a statement with a curly brace because that's assumed to be a block, so in this specific case where you don't have a declarator there, you have to wrap the whole statement in a parentheses, just to let it know this is an object destructuring, not a block label. This is part of our problem with overloading the curly brace to mean 15 different things in 15 different places. Okay. I don't regularly do this, most of the time when I'm doing destructuring I have a declarator there, but if you're not doing a delcarator, just remember the object needs the parentheses, the brackets don't. (Typing) If all this still feels too abstract, you're going to get an exercise on this in just a few minutes, so you're going to get a chance to practice it a little bit more, but it's going to take a lot of practice to get there. Trust me. It took me weeks of banging my head against it, and I don't understand, it seems the wrong order, whatever, I get it. There is a learning curve and it's a steep cliff. But what I'm really trying to get across is that the payoff is huge once you get there, that it really starts to simplify an awful lot of things that we've been doing very manually in our code, it starts to create more self documenting code, more declarative style code and that's much more readable.
Destructuring and Function Parameters
Everything I just talked to you about with array destructuring and object destructuring, in the declarator sense and in the straight up assignment sense, every bit of that is exactly and identically applicable to function parameters. We can do array and object destructuring inside of our function parameter list. So for example, I could say that I'm expecting to receive an array in the first argument position, the first parameter position of foo, but I want to go ahead and break that thing down, so I'm going to do an array destructuring to say that the first item should be a, the second one should be b, and the third one should be c. So if I print it out, console.log a, b, and c, if I pass in foo of 1,2,3, what's going to happen? (Waiting) Think about your processing. It's not array so it won't destructure. You have a number value, the number 1, that you then try to access a property on it. So you try to say 1 bracket 0 essentially. Have you ever tried that before, do you know if that works or if that's an error? I've not tried it, but I'm guessing you'd get undefined. Yeah, it works but we get undefined. We're not going to get the 1,2,3 that we're expecting and we're not even going to get an error here because we happen to give it a value that it's illegal, it is legal to do a bracket syntax against. Null and undefined, it's not legal to do that, but the number value it is because there's a thing in JavaScript called boxing, which is taking objects and boxing them into their object counterparts, so there's a number object that's boxed here and then we try to access the 0 property on a number object that's not present. Okay. So that's why it's legal and that's why I'm pointing it out is because you're going to run into this error and you're like why, where's my 1, 2, and 3? It's because you're destructuring a single value. In this case, you want to pass in an array. Okay. Now if we pass in the array, the array gets destructured into a, b, and c. And of course, this one should have a default value on it, because if you don't pass in the array, you want it to first pick up the empty array before trying to do the destructuring pattern. Same reasoning as before. And that only accounts for one argument, we can do multiples. We could have an array destructuring in the first parameter position, an object destructuring in the second, and a ...args in the third. We can do whatever we want, go crazy. Okay. This applies the same for objects? Yep, we can do object destructuring as well. So I could say curly brace a,b,c and what that would be expecting is to be receiving an object like this. (Typing) So here's why this is cool. Because this kind of usage starts to approximate a feature that does not exist in JavaScript, does exist in other languages, would be really cool if we had it, but now we have a way to sort of mimic it, which is called name arguments. It's different from named parameters. Named arguments are where at the call site of a function you get to say this value goes to this specifically named parameter. So isn't that kind of like what we're doing here? We're kind of saying the value 2 needs to go to the b parameter and the value 3 needs to go to the c parameter. They're not really parameters, they're properties on an object that gets destructured to look like a parameter. Okay. But in using object destructuring in our signature and by allowing the call site to use an object to do that mapping, we now are mimicking or approximating this idea of named arguments. How many of you have ever had to work with a function that had like 3, 4, 5, 10 arguments in it? And how many of you had to keep going up to the documentation and reading to remember because you could never remember? This basically accounts for 100% of the time that I ever wrote PHP code. I could never remember the signature of the function, I always had to go to php.net and look it up, no matter how many times I wrote it. A thousand times, I'm still looking up the same function because I can't remember is string first or is index first or whatever. It didn't make matters any better that they had multiple functions that did the same thing and arguments were in different orders and parameters in different orders. But the point is, it's kind of a code smell when you start to have like any more than maybe two or three at the most arguments for a function. If you have 4, 5, 10, 20 arguments to a function, nobody's ever going to remember them, and even if they could remember them, nobody's going to want to write null, null, null, null, null to skip all the ones they don't care about, or in our case we'd have to write undefined, undefined, undefined, undefined to skip over the ones that we don't care about, right? That's terrible. So my suggestion to you is one practical usage for this object destructuring is start switching any function that you have that takes three or more arguments, switch it to an object destructuring so that people can name their arguments instead. Which means, that I can skip the b one entirely if I don't want to pass it in, meaning that it could have a default value, and it'll pick that default value up. So I can skip over b entirely and I can list these in different orders. I don't have to remember what order they need to be listed in. I can list them in whatever order I want to. Okay. So you give much more freedom to the call site. That's one real practical usage for object destructuring is to use, is to embody this what we would call named arguments pattern. See I already forgot it, I told you I always forget this. We need the empty object there, in case somebody doesn't pass in the object, we want it to gracefully fall back.
Advanced Destructuring
This thing I'm about to show you is basically telling you how to do the exercise, so you're going to want to pay close attention because this is a huge hint, okay. There's one usage for destructuring that I have found to kind of be useful and it's in the case wherever I have an object, like for example maybe you've had this before, I have an object that has some properties in it that represent defaults for some settings, like maybe for an Ajax call or something. And then I have a config object where I'm going to populate only certain things that I want to set and what I want is for all the defaults to kind of get nixed in together with the ones that I've set. Anybody been in that scenario before? And you've probably used some library or frameworks like Mixin or jQuery Extend or I think underscore call is extend or whatever, some utility that mixes objects together in that way, right? You've probably done that before. The rub is that every framework and utility does it slightly differently. Some of them go from left to right, some of them go from right to left, some of them have different rules for if the thing is present, but it's an undefined value, does it overwrite it, if it's not present does it set it. All these weird things that you have to handle, so if you've got multiples of those utilities hanging around it can be hard to remember exactly what the behavior is of this particular utility in some corner case. So the point that I'm making is that destructuring actually lets us do what I think might be a declarative form of Mixin without using any utility at all. So I want to show you that pattern, and I call this pattern destructuring and restructuring. Okay. So let's imagine, I'm going to do the left and right thing here, let's imagine that I have a couple of objects, I have like a defaults object that says, I'm going to pretend in that Ajax scenario, let's say I've got a defaults object that's got like a method that defaults to POST and it's got a callback that defaults to some empty function and let's say it's also got a headers sub-object that has a content-type that defaults to text/plain. Okay. I'm just making some stuff up on the fly. This is my defaults object, okay. And then I'm going to somewhere else in my code create my config object, which is going to add some properties in that don't exist in the defaults, but any place where defaults is there and it's not in the configs, I want that mixed in there, right, so that my compound or my mixed in object has all of that stuff present. So I might make my config object and that one's going to have a URL and I will go ahead and set my own callback is equal to foo and I'll say that I want to add in a headers for x-requested-with, I'm just making stuff up, I don't know. Okay. So there's my config object. Now I need to mix these two together, everybody with me? I need to mix these two together, I could use some kind of extend library or utility or something, but I'm going to show you how we might do this with destructuring. So what we could do is I'm going to create myself a block, an explicit block, so that I can use some block scoping, and I'm going to use my let declarator because I'm going to create some temporary variables that I don't want to pollute the outer scope, okay. So my temporary variable is going to come from an object destructuring and what I'm going to be destructuring is the config object. Are you with me? So what I'm going to be doing is I have to know in my head the union of all the properties that I need to account for because I don't have a ... props thing that can collect other ones that I don't know about. So in this particular case, if I'm doing this with objects, I'm going to need to know, but I know that the ones that I've written here are the only ones that we care about for this example. So I know I need a URL. Either the URL will be the URL that I've provided from the config URL or it might be a default URL if there was one, but you notice I didn't specify one. So I'm just going to list url. I need to pull that one out. Meaning, this is kind of required, like you have to get it. Okay. I could also pull out the callback, but in this case I do have a default, so I can set the default equal to defaults.callback. And then I'm going to pull out a headers and this one I'm going to go ahead and destructure, so I'm going to say that the content-type is going to, that property name isn't a valid variable, so in this case I'm going to need to give it a target, so I'll call it contentType, and it also has a defaults, which is defaults.headers of content-type. Okay. I know because of my screen resolution here, things are getting harder to read because I made these long lines, so I'm just going to start breaking some stuff out to help the readability a little bit. Okay, but now I've accounted for that particular headers one. And I also know that it is possible to pass in the x-requested-with, even though that doesn't have a default, I need to map that to something, so I'm going to California that xRequestedWith. Okay. And finally, I need to account for method. Method is either going to be what I passed in as method or defaults.method. So I have manually accounted for all the combinations, and what I've done is I've taken advantage of the built in default behavior checking that already exists in destructuring. I don't need to reinvent all that logic, it's in JavaScript and I'm taking advantage of it. And I am getting a whole bunch of individual variables, so I'm going to have a method variable, a URL variable, a callback variable, contentType, xRequestedWith. Those are all going to be let declared variables that are hidden inside of this outer curly brace. Did I lose you entirely or are you tracking with me? Okay. So we've broken down our thing and in the process of breaking down, we've done this mixing by taking advantage of the default idiom that's built into destructuring. The last step is then to restructure, to put all that stuff back into our object. So now all I have to do is say confg is equal to and I need to put this stuff back together. Now I need a method property, so I could say method: method. We haven't talked about this yet, but ES6 also gives us concise properties, which means if they're the same we don't need to list them twice, so I can just say method and then I need url and I need callback and I need a headers that's an object that includes content-type and x-requested-with. So now I have restructured that object back and now it's, the two have been mixed together. So I didn't need a library method, and I'm entirely in control at each one of these steps of how that mixing happens. I don't have to rely entirely upon the algorithm built into the library, I get to decide for each one of them how that mixing logic occurs. But also, what this really starts to point out is that I don't even really need the defaults object anymore. Because wouldn't it make a whole lot more sense for me to just go ahead and inline those defaults right here? Doesn't that make the code a whole lot more self explanatory? Yes. (Typing) This one doesn't have a default. So now we don't even really need to do that whole defaults object mixed together, we're just mixing the default values into our config object using destructuring and restructuring. Now it's debatable whether or not you think this is a win, but I think not having to worry about some library and some internal quirks and being able to do so with declarative syntax makes the code more self documenting.
Exercise 3
Let's go ahead and open up exercise 3. We sort of have almost the same thing, expect in this case there's a little bit of foo baring going on, but we have almost the same thing going on, so let me orient you. What we have is a check function that's going to pass in an object, but that response function is receiving an object. Okay. So stick with me. What you want is you want, right here, you want to do some object destructuring, and right here you want to do some object restructuring, Exactly like I just did for you before.
Exercise 3 Solution
Let's talk about ES 3 fixed, just to keep things short and sweet, I will just show the fixed version, but I'll talk you through it. So what I decided was to do object destructuring on the parameter that comes into the response because, remember, the response is receiving an object, so let's go ahead and just destructure it while it's coming in. This lets me not have to do any let or block scoping to contain those temporary variables because they're just going to be contained within the response function itself anyway. So I'm going to have a foo variable that defaults to whatever defaults.foo is and a bar that defaults to defaults.bar. I'm going to grab baz as is, which is just an array, and then I'm going to do these destructurings down inside of bam. Don't forget to put on your default objects so that your destructuring gracefully fails, okay. Now to do the restructuring, I can use those concise properties because I named them all exactly the same as the properties, so it's just foo, bar, baz, and then the qux and qam are inside of the bam object. Again, this is one of those places where I probably wouldn't keep all those defaults in a separate object if I didn't need to. I'd probably just go ahead and put those defaults right here, for example. Yes? Question from the room. Mike is asking - I don't know if I missed this, but in object destructuring, if there is a listed property not on the object, but up its prototype chain, will the value from the prototype be used or will it be undefined? Good question. So let's assume I did something like this, (Typing) so I'm asking for the a property on obj. If we defined obj like this, we know it doesn't have an a property, so it's going to ask for that a property, if it doesn't find it, it's just going to get undefined. But if we made object, if we made that property by doing a linkage to another object which did, and then as asked for the a property on it, if we did that manually, if we said obj.a, what would we get? Obj doesn't have an a, but it's prototype linked to an object that does, so it would find that a property, right? Same is going to be true in destructuring. If we ask for an a property on an object, but it doesn't have it and yet it finds it on the prototype chain, it'll pull that one out. So again, just think of destructuring as a declarative syntax for doing all those assignments and then the behavior of those assignments is exactly the same as if you did it manually. Any questions about destructuring? If it's still like 80-90% fuzzy, don't worry, that's completely natural, it's going to take a little bit more practice. But of all the things I'm teaching you, this may not be like the low hanging fruit stuff like ... or the ... operators and defaults, those are low hanging fruit, those are easy. This is not low hanging fruit, but it also has the biggest payoff, in my opinion, in terms of code readability. So it's worth the effort.
Template Strings
Concise Properties and Methods
So, the next thing that we want to turn our attention to, I've already kind of alluded to this a little bit, but I want to talk to you about a few extensions that have been added to the object literal syntax to give us the ability to do more declarative stuff instead of the more manual, imperative stuff that we've been doing all along. So, I'm just going to talk to you about a variety of quick little fixes or extensions, if you will, to the object literal syntax. So, it used to be the case that if I was making an object and there was some, oops. There was some variable of a name, for example, a, and I wanted to put that thing into object, if I wanted to say a property of that same name with the value of a, I'd had to say it twice, I'd have to say a: a, right? We now have what are called concise properties which we've already seen which is in the case where the property name and the value it's coming from are the same, we can just list it once. So, that's a concise property, okay? Nice simple clear win. In addition to concise properties, we also have what are called concise methods. It used to be the case that if you wanted to put a function inside of your object literal, you had to say b: and then put some function. Now we have the ability to do what's called a concise method which is you take out the word, function, and just put the name in the parentheses and the curly braces. Okay? I use these a lot. But there's something that you should be aware of with concise methods. You might be of the impression since there's a name there that the extension of that would look like... Where there's not only the property name but also the name of the function. You might be of that assumption. And unfortunately, that assumption is incorrect. Because what actually, by spec, happens is that b property points to an anonymous function. Now, it is an anonymous function that has name inferencing. So, that is an anonymous function that will show up in your stack trace called b. But here's where this falls apart. Let's say you have code like this, because I do that regularly, where I have the property name and the variable name, and why do I have that variable name? 'Cause there's some crazy condition where I want to do a recursive call on it. Or, it's an event handler and I want to unbind it, for whatever reason, I need a self reference to the function from inside of itself, okay? So, if I want a self reference to the function from inside of itself, this is nice and helpful. As soon as I come along and do this, uh oh. b is no longer a lexical identifier so now my code's going to stop working. So, the caveat, it's a big caveat, the caveat to concise methods is they don't have a lexical self reference. I think this is dumb, I petitioned very hard for them to not do that. But there is a reason why they didn't do it. And the reason is, that it is possible for you to make functions, for you to make property names that can't be lexical identifiers. For example, that can't be an identifier because it has a space in it. And it is entirely legal for you to do that. So, they ask rightly the question, what should the lexical name be in that case and there is no good answer to that question. So, because there are some cases where they can't come up with a good lexical name, they didn't ever put in a lexical name. It's kind of a baby thrown out of the bathwater sort of scenario. So, I find that kind of annoying that these are lexically anonymous functions. By the way, I gave you this admonition earlier, let me just remind you, this is one of those cases where you're going to go over to Babel and you're going to be like, "Oh, but, "see, it makes a function b there. "You must be wrong." No, this is a case where Babel is deliberately skirting around that part of the spec because that is the quickest and easiest way for them to do named functions. Named functions are kind of hard to do if you don't actually put a name on them. So, they take the easy short path. If you try to do something like this, look at what they do. A much longer, more complex path for getting a named function that preserves the correct behavior. So, don't come to me and complain that I don't know what I'm talking about. I do, I've studied it, trust me. Concise methods are lexically anonymous. If you make that property name, "Hello, world" like you had, do they not bother to try with the name? They came up with their own, that's not what the spec says. Okay, so, concise methods are cool, I use them a lot, but in any case where I need a lexical self reference, I don't use them because they're lexically anonymous. Alright, concise methods, concise properties, nice easy wins. Another case that is fairly common, and actually probably more common than those, is where I have something like this, and what I want is I want the property named, Hello, to hold some value. But the property name is currently in a variable. So, what I end up doing is saying, obj of c is equal to some value. I have to do it outside of and after the object literal. I don't know if anybody else has been in that case, but I, probably five times a day, I run into this case where I'm trying to put something in object literal and I can't because the property name needs to be computed. Well, now we have computed property names. So, you could put the brackets syntax there inside of the object literal. Inside of the brackets, you can put any valid JavaScript expression. Here, I'm putting a variable, we could do some crazy like, .toUpperCase, for example, any JavaScript expression that you can think of. Computed properties might be in my top five favorite features. Just because I don't want to have to always be adding those on the end. I use it pretty frequently. Alright, so we have concise properties, concise methods, we have computed properties. Computed properties can also be used for method names. So, we could say c+"fn", so now we have a Hello, fn, is a concise method, for example. So now, there's a function called Hello, fn on our object. If you use the concise methods, is that equivalent to the full function body versus like the arrow syntax method? Yes, concise methods are equivalent to function bodies. There's a couple of differences, nuanced stuff that we're not going to talk about today, like the way the super keyword and stuff like that behave. But for virtually all cases, the full function is how concise method behaves. Right, so you still have to use return keyword and things like that. Yep, yep, you do, yes? _____ is asking why do they even bother naming it? Who's they, are you talking about Babel maybe? That was me, yeah. Oh, why is Babel naming it? Yeah, in that example you had up. I have no idea, because that's what Babel-- Because they don't really use it for anything. Are you saying why are they naming it here? Why they add the second-- Oh, the reason they add it, I mean, I know the answer to that question. The reason they add that is, in this case when a call stack happens, we want for that b function to be reported as the name b, so that's why they add it. What they're doing is they are transpiling ES6 name inferencing. Yes? Question from me. The last one on line nine, if you wanted to make that, say it was already cursive function. How would you call that? How could you call that? For a computed property, you'd have to recompute it on the inside. So, you could say something like, object of c plus fn, like that. This is deprecated, but you could do arguments.callee. It's not a good idea, there's a proposal, I think, for a future version of JavaScript where we're going to get these special meta properties and I think it's going to be something like function.callee that we'll be able to use maybe in the future that'll be a self reference that doesn't need a lexical name. Okay, but otherwise, it's just, you have to recreate it and-- What's that? You have to recreate it, really. You have to recreate, yeah. Or use arguments.callee. Which means it's not a lexical reference, it could be a different function. Yep, yep, exactly. So, like I said earlier, if I have any inkling that I'm going to need a self reference for any purpose, I don't use concise methods. We haven't talked about these today, we will later today. But there are what are called generators. That's a special kind of function, and you can specify a generator, for example, foo. Generators use the star symbol, function plus star, to signify themselves. There's a concise form of this, which is weird, if you use the concise method, you take that star and you move it here. So, this is a concise generator. And we'll talk more about generators later. And finally, there is concise computed generators. So, we can... For example, do this. So, that is a computed concise method that's also a generator, computed concise generator. There are a few other object literal extensions that I want to get into, stuff like proto, and super, and stuff like that. Those are covered in my book if you want to read more about those. But these are kind of the right parts, the places that you'll often find yourself using to improve your usage of object literals. A little bit goes a long way in terms of just constantly having to do the old way versus this new way, it saves a lot of work. Any more questions about our object literals? Do you know if they're planning on working in a syntax for async functions into this kind of? There is... I think async functions are just this, but I have to check this back on that, I don't actually know. That's not yet in the spec but it's almost there.
Template Strings
We're going to go back to our little side-by-side adventure here and do some more comparison. We're going to talk about a simple, really, little feature added in ES6, but it's one of those delightful features. It's not necessarily hugely groundbreaking or going to radically change the way you approach your code or even express things, but it's more delightful in the sense and that does create less of the visual clutter that our readers need to do deal with. So, let's talk about a scenario like this. Let's say that I have, name = "Kyle"; and let's say orderNumber = "123"; and total = 319.72; let's make it 319.7. Alright, so if I were going to make a string to print out some message, say I was printing out a receipt, if you will, it's going to print out some message that included these values along with some text. That message might, for example, look like this, msg = "Hello," + name + ", your order" and at this point, I'm running out of line room so I want to break onto the next line so I'm going to do, you might have seen before the trick of breaking onto multiple lines by putting a backslash at the end of the line, officially, that's called a line continuation. So, I'm going to break onto the next line, I'm going to say, "Your order" and I'm going to say plus orderNumber, was, and now I'm at the end of this line so instead of doing a line continuation, I'm just going to do a plus, go to the next one, and I'm going to, let's go ahead and put the dollar sign symbol in here for this matter. So, your total, so I made this string put some values in, making this up on the fly, so. Okay, so let's evaluate what's happening here. What's happening is that I am what is called interpolating these values into the bigger string. That word, interpolating, is not something I made up, that's a thing that other people have made up. It's an actually feature in certain languages, and we're doing so manually with strings and string concatenation because up to this point, JavaScript hasn't supported any way to do interpolation. So along comes ES6 and they decide to invent this feature that's going to allow us to do interpolation, but instead of calling it interpolation, they give it this other name, which is really terrible, and confusing, they call them template strings, or template literals. Now, the word, template, has a fairly strong meaning to most people. And I'm willing to bet that you listening to this, you're probably, when I say the word, template, what comes to your mind is the idea that I have some sort of basic set of value that I can recreate every time and give it new values to be inputting into it. So, I have this thing that I can remake over and over again with new data every time, that's what most people think of when they think of a template. So, when I show you this feature and I tell you that they called it template strings, or template literals, you're going to think, "Oh, I'm creating this template "that I can re-render my message every time," and that's not what they mean by the word, template. Okay, so before and after here, let's start out with the same vars but now to create my message, what I'm going to be able to do is interpolate these variable expression directly into the string. In PHP, you might be familiar, if I'm recalling correctly, it's in double quotes, if you're doing a dollar sign variable name instead of a double quote, it automatically replaces that with the value of that variable. That's automatic interpolation. So, we're going to do something of the sort here, but we can't just do that inside of double quote strings or even single quote strings in JavaScript because that would break existing code. So, we had to come up with a new syntax for delimiting these special kind of, what are called, template strings. And that new syntax is to use a back tick instead of a single quote or a double quote. And now, we don't need to do any of that breaking out of the string to add it things, so I'm going to get rid of the adding. And what's interesting now is I don't need to escape the end of a line because these are automatically capable of bridging across lines, and I don't need this stuff. And, I don't need that, and then I simply end it with another back tick. So, there's my new message, now, if that was the way that it worked, that could also create some havoc because how would it know if you really wanted the variable name replaced, or you wanted to say the word, name. So, we need to delimit these in some way. And the way JavaScript chose to delimit them is to wrap dollar sign curly brace around the expression that we want to be interpreted and interpolated. So, we would say, ${orderNumber} and ${total}. Notice there's two dollar signs there. The first dollar sign's left entirely alone, it's only going to pay attention to a dollar sign curly brace. So, if you wanted to do a dollar sign curly brace that wasn't part of a delimiter, you just have to backslash the dollar sign. So, no we have this string which I can break across multiple lines and I can automatically interpolate those expressions directly into my string literal. The difference here is going to be subtle, but while we're talking about it, we should go ahead and talk about it. Have any of you ever heard before the notion of the phrase of a multiple-line string? This phrase is often used in programming, but it's used imprecisely. We're actually pretty famous about this as developers, we use these different words, and people mean slightly different things by the term, and it propagates so much that actually, it turns out that the word hardly means anything because it means so many different things to different people. So, I just want to provide some clarity on this. There is such a thing as a string that is continued onto multiple lines, and there is such a thing as a multi-line string and I'm going to assert that those two things are distinct concepts. So, a string that continues across multiple lines would be like what we see here, which is that I do a string continuation, or a line continuation at the end, and I come out over here, and if I were to run this code in the browser and print it out, let's look at what happens. If I run this over here in my console, and print out message, I get one string all on one line, everybody with me? But, people will call this a multi-line string. Well, there's not actually multiple lines in the string value. So, it's not really a multi-line string, it's a string that continued across multiple lines. The nuance that I'm trying to get here will make more sense in just a moment when I talk to you about the template strings, the template literals. But it's important to note that this one does not include the new line because we are escaping the new line that we put there. So, it's leaving it out of the string value. Are you with me? Now, let's talk about template literals. If I were to take this code and run it, you'll notice that I did not escape anything on my new line. If I were to take this code and come over here to my console and run it and then ask for what's in message, you notice anything different? Now I actually have a new line at that location which is why it prints across two separate lines. So, what I would argue is that a template literal, or a template string, is in fact, if you put it there, a multi-line string, a string that preserves those new lines whereas the old school JavaScript strings with line continuations are just simply strings that span multiple lines. We get a difference here. If you didn't want that string there, I mean, if you didn't want that new line, you'd have to escape it. In which case, it's not going to be present. But by themselves without any doing of that escaping, you're going to get a real multi-line string with a template literal whereas with regular strings you don't, yes? Do you know, for the spec, does that insert a /n or is it like OS dependent. It's actually the \n. Okay, always? Yeah, they stay (mumbles) Okay. Alright, so just a little nuance for you to pay attention to. If you're going to break your strings across multiple lines and not escape them, you're going to get actual multi-line strings, which may or may not be what you want. You should be careful about that, okay? Alright, so now let's talk about why this is bad to call this a template string. Why is this a bad name for it? Well, it implies that message is a template that can sort of be re-rendered with different data. But message here is an actual legitimate real string value. The back ticks here are a way to do a string literal that is evaluated immediately and then it's done and it's never re-evaluated ever again, it's kind of like an iffy. It just runs right away and then it's done. So, the way most people think about templates with re-use as part of the definition, these are not re-usable in that sense. You could wrap them in a function, and call a function and every time, get back a new string from a new template literal. But now the function becomes the template, not the string value. Are you with me? So, I and several others have regularly called out this is a bad name for these, template literals, any usage of the word, template, here is going to be confusing and misleading. We brought these objections up a while back, but the spec was too far along and nobody seemed to care about actually changing anything, so it landed in the spec as template literals, or template strings, or something like that. So, it's kind of annoying, but I prefer to call these what they really are, just interpolated string literals. Call me crazy, I like to call an apple an apple, right? That's what it is, it's an interpolated string literal. Interpolated string literal's a big old mouthful. So one day, about a year and a half ago, I was tweeting with Brendan Eich, as I do, because he and I are best buddies, he's a cool guy, anyway, I was tweeting with Brendan Eich about this because I had been kind of tweet complaining about template literals and what a bad name that was and surprisingly, he kind of agreed with me that yeah, there can be some confusion around that word, template. So, we kind of decided in that Twitter conversation that if you want to be a cool kid and you want to use the right name but you want like a cool, hip-sounding name for it, you don't want to say interpolated string literal, you can just call them interpoliterals. So, I kind of liked that interpoliterals, that's kind of a cool word, anyway. Maybe you don't think that's funny, maybe you haven't had enough coffee this afternoon, I don't know. Interpolaliterals, interpolated string literals, whatever you want to call it. Okay, so these are nice, like I said, just in sort of a basic feature that's kind of delightful to have a little bit easier syntax for dealing with our strings, your error messages, your whatever. I'm not at all suggesting, as some do, that you ought to do all your HTML structuring inside of your JavaScript, I am actually very anti that. But there are a lot of times that we do string constructions of error messages and other things like that. Any time you're doing any kind of string construction, a template literal, an interpoliteral is going to be more helpful for you. Questions about this? A question in the chat was, "Can this be anything?" Yeah, and actually, I meant to say this, right now I'm using a variable name, it's any valid JavaScript expression. So, you can put an entire JavaScript program inside of the expression if that's what you wanted. As matter of fact, as crazy as this sounds, I've had a couple of occasions where I did an interpolated literal inside of the expression inside of another interpolated literal. So, you can go, it's turtles all the way down, you can go down as far as you need to go, okay? But most of the time, probably 99% of the time, you're going to see them just as regular JavaScript variables.
Tag Functions
So, let's talk now about an extra feature that's included with interpolated literals that is probably not terribly self obvious what this is or what it's for. It is possible for me to put an expression directly in front of the interpolated literal, and that expression will act as essentially a function call. So, you're thinking, well, what is the foo function? It would be a function that we would have to define and you might assume that that function would receive the interpolated string literal as its argument. It's not really quite that. It's sort of like that but not quite. So, let me talk to you about what this foo function would do. This foo function is going that receive as its first parameter, as its first argument, by convention, we like to call it strings. I mean, you can call these whatever you want, but by convention, most people call it strings because what it is is an array of all of the string literals from here. So, it is going to be an array that includes that string, and that string, and that string, and that string. So, it's going to have four string values in it. It's an array of four string values. The next thing that we would get, obviously, we're going to get these values in, you might think we would get a values array. For some crazy reason, they decided not to collect all the values up into an array, so what you actually get is like value1, value2, value3, all the way out to however many values there actually were, which is stupid and nobody wants to deal with those individually, so we all just collect them open to a values array using the ... gather operator. So, virtually, all of these, these are called tag functions, virtually every tag function you'll find, the signature will look almost identical to that, called strings, and then ...values. Obviously, you can name them whatever you want, okay? But what's the purpose of this function? Well, I can do really evil stuff like, "I am evil"; if I return a different value like for example, that string, and we ask what's in message, what do you think's going to be in message? The string, "I am evil," it has absolutely nothing to do with what we passed in. I don't think it's a good idea, but you can. The point I'm making is that the tag function serves kind of like a pre-processor. It's almost like a macro, it pre-processes the string before it's finalized. Which is going to allow you to do some actually very interesting things. Before we talk about the interesting kind of stuff that we can do with them, let's just think about what's the very simplest approach for me to construct these things together. For example, let's observe that the strings array is going to have four values in it, how many values are going to be in the values array? Three, right? Four strings, three values. Even if we had an expression at the very beginning of our literal, there would still be an empty string in the strings array there, and even if we had an expression at the very end, there would still be an empty string there, so it will always be the case that the strings array has one more value than the values array. That is an invariant, it will always be true. There will always be one more string then there is a value. So, you can probably realize if I had four strings and three values, what do I need to do to them? Loop over them and interpolate them, right? Concatenate them together. So, the pass-through version of foo, that is the version of foo that acts as if it's not even there is to simply loop over the strings array and interpolate in the values, totally untouched. So, let's write that logic. We'll have a string that we start out as empty, we do a for loop, for var i=0; i