What do you want to learn?
Skip to main content
by Kyle Simpson
Resume CourseBookmarkAdd to Channel
Table of contents
Speaker Introduction Part 2
ECMAScript Language Specification
Compiling Function Scope
Execution of Function Code
Scope and Execution Example
Function Declarations, Function Expressions, and Block Scope
Cheating Lexical Scope: eval
IIFE Pattern Questions
Block Scope in ES6
Problems with the Let Keyword
So I'll orient you to the exercise. What you want to do, and again, as a reminder both for you here in person, as well as for you listening in online, if you only have one copy of that exercises folder, before you start modifying things, I recommend just making a separate copy of your exercises folder. That way, at some later date, you can go back to a pristine copy of these exercises and try your hand at them. But we're going to open up... I got to go to day one. We're going to go to the exercises folder, go to EX1. You can open up the readme file and the JS file. All right, the readme is going to explain what you need to do. But also, you're going to come over here to your browser and open up EX1.html, which is going to load your EX1.js. And you're immediately going to get an error on the very first line of your program. That's on purpose, okay? So what this says is it wants you to fix the code so that our that our program will correctly print out the full alphabet, a through z. That's what the program is designed to do. Right now, an error is out. We got to fix those errors. This is asking you to take what you've learned about scoping, about hoisting, about IFFEs and all that other stuff. Put all that stuff together into a way to solve the problem. Now I have constrained your ability to solve this problem. You're not allowed to just put console.log a, console.log b in a program to solve it. So I've constrained your solution. You cannot have any global variables at all. If you look at the file, you'll see everything's global. So first thing you're going to have to solve is that you have to stop having any global variables at all. The second thing is that you cannot delete or combine any of the existing function declarations. You have to leave the function declarations or whatever as they are, you can't delete or combine them. You cannot create any new functions, except there's an exception that you can create IFFEs. And that's a big hint because you're going to need to create some IFFEs. And you cannot rearrange the order of any of the declarations. You have to use what you know about the way hoisting works to solve your problems. The things that you can explicitly do is you can declare extra variables as long as they're not global. You can modify a function declaration and turn it from a declaration into an expression, or from an expression into a declaration. And you'll probably need to do that. You can add or remove various statements or expressions like the returns or IFFEs or parameters. And the explicit answer here is you want to search for making the fewest changes possible. This does not call for you to change every line of this entire program to solve the problem. So go through it one at a time. Try to constrain yourself to these rules and see if you can get it to print out a through z. I will walk you through an answer to this in a few minutes right before we take our lunch break.
Exercise 1: Solution
Going back over what we talked about, the first thing that our readme tells us is we can't have any global variables at all. What was our most likely solution for preventing global variables? IFFE. Anybody remember that? So if we were to simply put an IFFE to start our program, go to the very end of our program and finish our IFFE, and then for good measure, I might indent all of this stuff but not technically required; now, everything is private, okay? So if I were to save this code and run it... Actually, let me do one quick thing. Before I start making changes to this code, I'm saving them. All right, so if I were to save this file and run it, now I didn't actually fix my bug, but now you'll notice obviously we're on different line numbers because now I've solved the problem of global variables. So my next problem is well, I know line three is causing a problem because it's trying to do a, and a is a variable. It's a function expression. So I said one of the things that you're legally allowed to do is change an expression into a declaration. So if I change that to just simply function a, now that we don't need the semicolon anymore, and I save my file, now a worked. But b is broken. All right, let's look for b. What's the problem with b? B is down here, also listed as a function expression rather than a function declaration. So let's change b into a function. Now you may be tempted to start fixing a whole bunch of these. But again, try, in the spirit of the exercise, try to fix as little as possible. Okay, now we got oops being printed, not an error. How is that happening? Well, it's happening because we got our function here that's printing out oops. But we are not allowed to change that, and we have a variable c. Let's see, if we've got a different, oh, we've got one down here. What happens if I change that into a declaration? Does anybody know how that might work? Because of hoisting, it's going to override the previous declaration. So now when I save my code, now I get uh-oh, undefined, not a function. So now I'm getting a problem with the function d. Why am I getting a problem with the function d? I'll just verify here real quick. I'm trying to call d... So I'm trying to call d way up here at the top before d can actually run, because d hasn't been given the value yet. Does everybody see that? Everybody see why that's true? Because the d gets, the capital D gets his information for the function, he gets that from this line. But we're trying to execute ourselves up here at the top on this line. So I could start trying to solve all kinds of other problems but I could also observe that maybe there's a trick that I should pull about not executing my function at the beginning, like I'm doing. Okay, what if there was a way to take out this function call entirely and execute it in a different way? So what if I were, for instance, this is just one of possible ways to do it, I could return that a function from my IFFE, and I can execute my a function at the end of everything, rather than at the beginning of everything? So if I save that code, again, not the only way to solve that problem, but now I've made some progress. Now I've gotten the e, e is trying to do something that they can't do, so let's go and look at the function e. E is trying to call f, but f is still... Isn't this capital F yet. I mean, the lowercase f hasn't been given the value f yet. I need to double check myself real quick. I'm forgetting in my fixed version one of several ways that I can solve that. Ah, I remember now, okay. So another way of solving this is to deal with the fact that the problem is that f isn't getting the value early enough. But f is a parameter name, which means I could pass in the function itself. So if I came here to e, (mumbles) e was... Who calls e, sorry. The d function. If I came here, if I passed in the f function, now he's going to get assigned before he tries to be called. All right, now g is having trouble, so let's go and see what. G is having trouble because it's trying to call h, but h is a function expression, so we can change h to simply be a function declaration. So we take advantage of hoisting. Okay, well, now I made it all the way to k, but it just stopped, it didn't do anything else. So what happens with these k values? Well, oh, I've got to do statement that says I need to call the next function. So what is my call to my next function going to look like? Well, if you were looking into this, you would see that I was creating these functions from my string by saying rest of i. So... Rest of i plus one, oops. So if we put in that call, now everything finishes to the end, and I've got a through z. However, there's still a problem. Anybody know what the problem with this code is? (audience muttering) Can't do global variables, exactly. So one way of solving this without very much change to my code, is to create an object for those variables, for those functions to be assigned into. Double check, what did I do? (audience member muttering) Ah, you're right, absolutely, good catch. So now I need to call object.k. There we go. All right, that was the spirit of the exercise. I know it's kind of a silly one, but it was to get you to think about your hoistings, get you to think about how you can play with scope. You notice, a bunch of these we left as expressions because one of our solutions called for simply executing the a at a later time.
So I want to take just a little detour here and talk about some binding confusions. And I'm not just making this up. This is actually, I mean, this is fake code, but this is distilled from an actual question on Stack Overflow that I ran across, and it's not the only one where people try to create this connection between these two models that we've presented between the lexical scoping mechanism and the this scoping mechanism. And the way that it usually occurs is somebody has some other function somewhere, like baz, the baz function here on lines five through seven. Maybe that one's declared as part of a third-party utility that they don't control. So they don't have any control over it, but they know that, that function makes a reference to something like this.bar. And then they have a function that they do control, like the function foo, and they've got a local variable inside of their function called bar, and they want to be able to invoke that function in some way, that when he makes a this.reference, it will reference their own local copy of the variable, okay? And that may or may not be something you ever attempted to do, but people attempt to do this, where they try to take some function and make it reference their own local lexical environment, make the this reference somehow cross over to their own lexical environment. What I will state plainly is that it is impossible to create a crossover between the lexical environment and the this binding mechanism. They're just two fundamentally different mechanisms, and they don't crossover. But there's an attempt that people make to do this, and it's a well-meaning and well-spirited attempt. Here, I'm trying to say I want to call this function in such a way and make it so that, that bar variable references my bar variable. And so what you can see here is they're trying to get the this reference to somehow magically reference their local lexical context. We don't have any reference to our own local lexical context. It's a thing that exists, but we get no reference to it in our code. So we don't have a way to tell them, make the this point to my own lexical context, and yet people attempt to do this. So this question was posed on Stack Overflow. I'm attempting to do this, and again, it was a different form of the code, but I've distilled it down to the problem. This is what I'm attempting to do, and it doesn't work. And I was dismayed to flip, you know, to scroll down a little bit and to see the first answer was the one with the big green checkmark on it, and it literally had several thousands upvotes. And I looked at the code that was posted, and I'll show you again the distillation of the code that was posted, it is abjectly wrong. And yet thousands of people have upvoted it as the correct answer. And people are giving it the green checkmark and saying, well, this is obviously the right code. It's a testament to the fact that people are not rationally thinking about the this mechanism the way it actually works. They're thinking about the way they want it to work. So I just want to caution you against that. I want to call it out. I want to show you how incorrectly they tried to solve this problem. This is the distillation of the way they tried to solve this problem. They said, okay, well, what I really need to do is I need to make that baz function, I need to make a local reference to it. So I'll say this.baz is equal to baz, and then I'll invoke that function as my own local context. And that will somehow magically kind of import that function back into my local lexical environment, and then therefore, it'll be able to reference the bar. Again, this is the answer that got lots and lots of upvotes. It's like nobody actually tried the code because the code is still abjectly broken. It doesn't actually work. But everybody thought this was the correct answer. And I'll explain to you, based on the same rules that we've already learned, why this is abjectly wrong, why this doesn't work, okay? In the previous code, we were attempting to somehow make the this reference here on line seven reference our own lexical context. So the way they tried to solve that was say, well, if I make a this reference here, and I sort of import the function, that should work. But as I already explained to you, the this reference gets set by the call side of the function call. So to ask on line three what does my this reference look like, I need to ask where does foo get called and how does it get called? So we come down to line 11 and we say which rule would apply when line 11 executes the function? This is global. It's going to be the global because we're in non-strict mode, and the default binding rule will apply. Does everybody follow that? So on line three, we're essentially saying global.baz is equal to baz, but there's already a global.baz. This is a complete no op. It's nonsense. It's a testament to somebody not actually understanding the mechanism that they're writing about. So this is a complete no op, this line here. On line four, we're saying global.baz. Now when the baz function is called, what is the call site for baz look like? Which rule will apply? Default. Not the default rule, the implicit binding rule, because of, look at our call site, we have an object.reference here. So there's a containing object, but it happens to be the global object still. So it's still, just like the previous slide, it's still going to be global.bar here. It's not going to be the local bar that you wanted it to be. And no matter how hard you try, I put this challenge, I mean, I tell people not to do this, and people always take it as a challenge. Oh, I can figure out a way. I assure you, no matter how hard you try, there is no way to create a crossover between the this mechanism the way it gets bound, and your local lexical environment. Put metaphorically, if we go back to... Sorry, let me go back a few slides. Put metaphorically, what you're attempting to do, I assume that you probably have this here, but not (mumbles). Do you have those little cross bridges between tall buildings where you're on the 10th floor and you can walk across instead of going all the way down? So what you're attempting to do metaphorically is to create a cross bridge between your two buildings. And what I'll just tell you is it's impossible. You can't create a cross bridge between them. They're two separate mechanisms like they're two separate buildings, and you can take one elevator, or you can take the other elevator. But you can't take both of them at the same time. It's just not possible, okay? So your brains may attempt to do that, and if you ask a question at some point, and I refer you back and I say you're trying to create a cross bridge, it's not possible, that's why. I just have to remind you that regardless of what we attempt to do, there's no way to make those two, you know, mix. They are two separate mechanisms.
The New keyword
So quiz time. Without cheating and looking back at that slide, I want you to tell me those four rules in order. What are they? New? New. Explicit binding with a call or apply or the bind. Implicit. Implicit binding with an owning or containing object. Default. Default rule. Was there a question from the chatroom? No. Okay. All right, those are our four rules in order. How do you borrow a function through implicit assignment of the this? How did the implicit binding work? When you call the function, it was in the context of an object, so it had implicit-- Right, we mutated an object to put a reference to that function on the object so that we could then say object.method name. Exactly. So sometimes, it's okay to put a method reference on an object and kind of implicitly borrow it. Other times, that's kind of awkward but that is a way to implicitly bind. How do you explicitly bind the this keyword? Call and--. Call and apply. Bind, you know, implies that it does a .apply. How can you see a specific this to a function? Using the bind function. The dot bind utility. Now why was that beneficial? What was the usefulness of doing that hard binding? (audience member muttering) If you wanted to be predictable, you want your methods to always go against a particular object. You're annoyed that it's always getting lost. Creating a hard bound reference to that solves that problem for you. So that's a good thing. What's the downside of doing that? Give us flexibility. Yeah, the trade off... If you've gone to all the trouble, I want you to ask this as a thought question, if you go to all the trouble to put hundreds of annoying this.references all over your code base and then you find yourself doing everything with hard binding, why did you do a this binding? Why didn't you just use lexical? Because you're going down this harder path and then cutting off all the flexibility, which is the whole benefit and the reason why you use that mechanism. They're two separate mechanisms, they have two separate capabilities and trade-offs and things like that. Hard binding is one of those sort of cheating things where you're fixing one problem; but you're trading off that you're losing that flexibility. So I'm not saying that hard binding is wrong. I use it from time to time. But if you find yourself doing most of your code with hard binding, you might want to ask yourself, maybe I shouldn't use the this keyword at all, because maybe I'm doing it the harder way. Lastly, how do you create a new this object? By using the new keyword. Not a trick question, by using the new keyword. Exactly. By using the new keyword. All right, so that's our this mechanism. And again, to reinforce, you can see that the this mechanism is dynamic. It's a binding mechanism that looks for things at runtime. It's entirely based upon how you called something. You can have the exact same function called in four different ways and get four entirely different bindings. Does that make sense? So that's the contrast between that look up mechanism and the lexical scope mechanism, which was hardcoded at author time. It's not dependent upon how something is called or where, it's only dependent upon where it's declared, where you put those nested scope bubble.
More Closure Examples
What if we used that let keyword? Now, the way it looks is that that let keyword is binding an i to the for-loop. But actually, there's a very special behavior in the spec that says it's binding the i not just to the for-loop, it's re-binding that I for each iteration of the for-loop. So if we started using let i, all of the sudden it magically creates a brand new i for each iteration that our functions will duly close over. And it'll just work, without needing to insert the IIFE. So that's a pretty cool thing that block scoping plus loops kind of solves that canonical problem of not having enough scope in each one of our iterations. This would be, I don't think I have this slide here, but this would be no different than if we had said, 4 var i=1, and then inside of our iteration loop we had said something like let j=i. We would have been creating a j, brand new for each iteration. Like if on line 1 1/2, I said let j=i, and then instead of referencing the i's here inside of our function, we referenced j, we'd obviously be referencing a j that was created for each iteration. So we could do it that way. Turns out there's an extra special behavior that lets the for-header do that automatically for us. Yeah. So then if you used one of those transpilers to run let on a DS5, and on a long-running loop like through 10,000 elements, it's still got to crank out 10,000 exceptions to do that catch thing. Could that be an extensive performance hit? Because the left thing is currently a catch block, from a thrown in exception, see it'll generate, like 10,000 exceptions and catch them 10,000 times, right? So are you saying that if I use let i, regardless of the transpiler, you're saying if I use let i, is it a performance hit 'cause it's creating a whole bunch of i variables, is that what you're asking? Because when you're writing the DS5, the trick was to run it in a catch block, right? Mm-hmm. And to get there you had to throw in an exception, right? Mm-hmm. So you got to throw an exception 10,000 times, in a loop that loops 10,000 times, right? Yeah. 10,000 stack traces, right? Yes. I don't know, I-- So, if you put this code, as is, into the Google traceur-compiler, that's exactly what it would do, is it would put a try-catch inside of the iteration, it would run each one of the 10,000 iterations. If, on the other hand, you did something like I would suggest where I don't want, like let's say I was in a situation where I didn't need a new i, but I still wanted it to be block-bound. Then I could create an explicit block, using letter syntax, around the for-loop, and then it's just one try-catch instead of for each iteration. So, yes. The bottom line is that ES6 will have the capability to optimize that binding. Our transpiled ones will take a performance hit, but it's really kind of like, do you want the benefits of block binding, not do you want to take a temporary performance hit, 'cause eventually the performance hit will go away. But yeah, it's a good point. Alright, now given everything I've taught you about closures I want you to tell me if this particular example is an example, if this particular code is an example of closure. What we have here is we have one of those IIFE's running, you can see I have an IIFE running. I have a foo that's pointing at this object that gets returned, and this object keeps a reference to this variable inside it, and outside of my function, I'm able to reference something from inside of the function. So, by virtue of our definition of closure, is this closure? No. Why not? I think there would need to be something returned from an inner function. Or a function, or, I just don't know, but it doesn't feel right. (laughter) It doesn't feel right. Yeah. I wish that was a good enough answer, for like the real world. (laughter) I know, man. No, boss, I don't know why, but it just doesn't feel right. Something smells wrong. You got to give me an answer as to why, you got to defend your position. I would take the opposite position. You think it is closure. Yes. Why is it closure? It's accessing a variable it doesn't have access to its scope normally. Ok, let's go to the definition that I gave you for closure. What was the definition that I gave you? There were two characteristics for that definition. You have access to the lexical scope. What was the definition, can somebody go back and read it to me? It remembers its lexical scope-- What remembers its lexical scope? Function. When a function remembers its lexical scope, even when the, Function. Function is executing outside its lexical scope. Is there a function who is remembering his lexical scope? No. No. Because we didn't transport a function out. This is kind of what you were trying to articulate. There's no inner function here, that's being transported out. We are keeping an object reference around, this code works, you can run this code and it will work. We're keeping an object reference around, but we are not having a function keep a reference to a scope. So by definition this isn't closure, it is object references but it's not closure. Does that make sense? It's got to be a function being transported out.
Let's do a quiz. What is a closure and how is it created? In other words, I want you to give me back my definition. What is a closure, and how is it created? A closure is a function that can be called, that keeps its lexical scope when it's called somewhere else. Exactly. A closure is when a function remembers and accesses its lexical scope, even when that function is executed outside of its lexical scope. Ok, how is it created? When an inner function is transported outside of the outer function. Right? How long does its scope stay around? Until there's no longer any references to it. Yep, so what we basically said was, a closure is kind of like a reference to a hidden scope object. So as long as there's some function that still has a closure over the scope, that scope's going to stay around. But as soon as that closure goes away, scope can get garbage collected. Why doesn't a function callback inside of the loop behave as expected? What was wrong with that little for-loop with the set timeout inside of it? It wasn't actually creating its own closure? Yeah, there wasn't a variable created per iteration. Yeah. We thought that somehow magically it was, but it wasn't. How did we solve that? What was the canonical way that we solved it? The IIFE. Putting an IIFE inside of the iteration, that did it. Also, the other solution? The let. The let keyword, right? Putting a let keyword on the for header, or inside the for-loop, that also creates scoping per iteration. How do you use a closure to create an encapsulated module? There were two characteristics, what were they? It has to be wrapped by a function. Has to be an outer-wrapping function, that's the first one, the second one? Return one or more functions. Return one or more inner functions that have a closure over the scope. What are the benefits of that module pattern? Why is that beneficial? Create private and public. Hiding stuff, the principle of least exposure, hiding things, having a public API. It's the idea of not exposing inner details that you don't want people to accidentally rely on, undocumented features, all of those benefits. Can anybody think of a tradeoff to the module pattern? There's a couple of tradeoffs. One tradeoff that some people, it's not a tradeoff in my opinion because I have a different opinion about what testing means, but some people don't like the module pattern because it hides a bunch of stuff, and it makes that inner stuff hard or impossible to test. One of the benefits of having everything public is that everything's testable, right? So we have this idea of unit testing, and theoretically, unit testing says we should unit test every single function. I disagree with that definition of unit testing, to be personally honest. I think a unit is the smallest indivisible unit of code. So if a module has 100 inner functions, and only one public API, there's just one unit test, and it's for that one public API. That's my own take on testing, but some people think it's a downside that you have all that hidden stuff and you can't individually test any of the implementation details. So that's one tradeoff, anybody think of a different tradeoff? It might've been a little bit subtle, so I'll help you. Every time I create a new module, remember I have this function that can churn out a new module? Am I creating a whole new copy of all that internal functions? So let's say I had one little module factory, and I wanted to create 1000 instances of my module, out of 1000 copies of all of its functionality. So I'm creating a whole bunch of extra copies. We will see in the next section of our discussion, we'll see a way that that particular drawback can be addressed.
Alright, exercise two. I've listed it at 20 minutes. It shouldn't take you more than five. Because you're going to write less than eight lines of code. But, as they say, it's not, as they say, the important thing is which eight lines of code do we write? So, let's open up exercise two. You can open up the README, and the js file. I'm going to open up in a browser the fixed version of the code, because I want you to see what it's supposed to do. Well actually it doesn't matter. It doesn't matter the fixed versus non-fixed. Let's just look at exercise two. I'll open up the HTML file. It's a simple note-taker app. We have built in, you know, you can click on the notes, and highlight them, you can click out. You can add stuff, and, you have a little help dialogue that tells you what to do. So it's a simple little note-taker. You're not going to add any functionality to this app, but what you are going to do, is use the classic module pattern to organize the code better. So if we look at ex2.js, it's a whole bunch of global functions with no organization whatsoever. And what I want you to do, is using what I just taught you about the module pattern, go back and turn this into a well-formed module. And I explain how to do that. You'll have two methods on your public API, one will be called a nit. One will be for loading in data. You'll create yourself a little module, API, and then modify the code to do that. So, again, you're literally writing, like less than 15 lines of code. If you find yourself doing lots and lots of coding you're doing it the hard way. It's very simple, but go back to your slides where I discussed the classic module pattern, the characteristics of a wrapper function, and a return value and all of that. That should be more than enough to get you going. I'll give you guys maybe eight or 10 minutes, and then we'll come back.
Exercise 2 Solution
Prototypes Explained, Part 1
Prototypes Explained, Part 2
Prototype: Objects Linked
Linked Prototype Diagram
Here's that diagram, the one that I have up in the board in a much more complete fashion and showing you both the bar and the foo in play. We have an object. Let me use my laser pointer if you want to try to show up on the screen. I don't have line numbers to use so you just have to hear me. We have this square that's next to the foo that's foo prototype and it has a constructor back. And then we have a square that's next to the bar that's a prototype. But you notice the dotted line shows us the implied relationship because this guy didn't have a constructor on it so his implied relationship is that the constructor is foo. Same thing down here with b1 and b2 they have an implied relationship that their constructor is foo which is just nonsense. But we see the consistency that those bracket bracket p relationships link exactly where exactly we expect them to link, okay? Now unfortunately, this is kind of a fib because this isn't all that's going on in that previous code. I actually have to show you an even more complex diagram to let you see what's really happening with that previous set of code. You should note that some of this stuff will look familiar, we already see the foo and the bar. Those are still here. But now all this stuff up here in the top left corner that's all brand new. The whole bunch of more arrows and dotted lines. And your brain is probably going to start to explode at this point if you think about this diagram. This took me more than a day just to create this one diagram. It's complex. Now there are two takeaways to get here. The first takeaway is this is really complex and if you feel like that you want to deal with these mechanisms with all of this then you better print this diagram out and stick it on your monitor next to those other posted notes 'cause you're going to have to deal with this complexity every day of your life. That's one takeaway. There is another takeaway. There is a more optimistic take away from this. That even though there's a lot of complexity here there's actually an amazing amount of internal consistency going on. Because everything that we can observe about the language is explained by the exact same core set of rules that we've already talked about. For example, you remember when we talked about function prototype bind and magically Foo could call Foo bind? You may have wondered, how is that possible? This diagram explains exactly how that's possible because look at bar and Foo, they are functions or objects which means that functions have an internal prototype linkage. So they delegate up to the function .prototype. And that's where they get .call and .apply and .bind and so forth. So all of the mechanisms that you can observe about the language are explainable through these relationships in these arrows which is a nice internal consistency. There's not actually a lot of magic going on. It's all the same thing, but it's complex, it's difficult. And what we come back to is when it comes down to it, all of these circles and arrows are completely irrelevant. What we really only care about are these squares 'cause the squares are where all the action happens. The objects are where all the action happens. All this other stuff is a whole bunch of distraction that we use to get there. So wouldn't it be nice if we could just make the squares without all the other crap? And that's where we're headed. Yes? I'm a little bit confused on this question, but I'll ask it as best I can. Sartav Si is wondering in the previous couple of slides, the example, where you're assigning bar.prototype to object create Foo.prototype, if you were to reassign Foo.prototype later, would bar.prototype inherit it, or would bar.prototype be pointing to your original Foo.prototype? Kind of neither because inherit is the wrong word. Let me see if I can. What Chris W says there is correct. It creates a brand new object that has a prototype linkage to the Foo prototype object. It's not a copy, it's not an inheritance, it's a new object with a linkage. Hopefully that answers it. We'll see more about object create in a little bit. We'll come back to it. All right, so this diagram sucks. But there's hope because there's a way to get a lot of this complexity erased.
Quiz: Prototype Behavior
Firstly, what is a constructor? What's that? Property. Oh, I thought you said problem. I would agree, it is a problem. What is a constructor? It is a function that is called with the new key word in front of it. .constructor is a property, but a constructor, a constructor call, is a function that is the new keyword in front of it, that's it. What is bracket bracket prototype? What is that prototype linkage and where does it come from? Points to the prototype of the constructor used to create the object? Sort of. Bracket bracket prototype is a linkage from one object to another object. Where it comes from, there's two different ways that we've seen. We can get that linkage from object create which just links it to another arbitrary object, or we can get it indirectly as step two of the four steps of the new keyword. But either way we end up with one object linked to another object. Everybody following so far? How does that prototype mechanism, how does it affect how we deal with an object? Similar to a lexical scope where if you go up to the next object-- We can call a property or a method on an object reference, and if they can't handle that object or property, that property or method reference, it delegates up to prototype chain to a different object. Right? It's an amazingly powerful mechanism, this delegation thing that we're getting to, the fact that we can have these sort of, the object almost can be sort of a fall back object. But the fact that we can delegate from one object to another object is a powerful pattern. You notice in all of this I had to discard anything about copying or whatever. We'll come back to copying. How do we find out about where an object's prototype where that prototype linkage points to? There were three ways that we figured it out. We could give an a1 or b1. How do we find out where his prototype linked to? Dunder proto. Dunder proto. Object.getPrototypeOf. And what was that third crappy hacky way? (mumbles) .constructor.prototype. All right. Just as a reminder. I said that this keyword works really nicely, but just as a reminder we still have exactly the same problems with these bindings that we had before when we're only talking about these keywords. If you take a1.speak and you pass it into a click handler all by itself, what's going to happen too is this reference. jQuery is going to force it to be the button rather than our a1 object. So you still have the same problem where you need to deal with .bind, hard bindings or whatever.
This next exercise, again, probably shouldn't take the ten minutes but what you're going to do is essentially you're going to take the fixed version of what you finished in exercise two. You can either take my fixed.js or you can take yours if you like your code either way. But that's going to be the starting point for ex3.js. So when you open ex3.js you'll see that it's empty and it says copy in the fixed code as your starting point. So I will do that, I will take the fixed version from ex2. Copy that in as my starting point. Now what does read.me tell us to do? We're going to revisit the work from exercise two but we are going to take what we've just learned about the prototype and we're going to do away with the module pattern, and instead we're going to implement this as a class with prototypes. So I'm going to get you started and then I'm going to give you a couple of minutes so that you get some practice with it. So the first thing is that we no longer have a module factory. What we're instead going to have is a constructor which we'll call notes manager for convenience sake. Consistency. We'll come back to his contents here in just a moment, but now these things that were private methods inside of our private functions, inside of our module, these are now going to be public methods on our API. And the way we do that is NotesManager.prototype.addnote equals function that takes a note parameter. And then we un-indent one level. Add a semi-colon. Now in all places inside of these functions that they reference things that were private variables, they need to now reference properties under this object. So I need to say this.notes.prepend. I'll do one more for you. NotesManager.prototype.addcurrentnote equals function. Add my semi-colon. That new note, that needs to have this reference in front of it. This guy needs this reference. This guy needs this reference. And this guy needs this reference. So your exercise is to continue that conversion, convert from the module format to the prototype format and insert these references. Pay particular attention to your event bindings, make sure you're using binding where you need to, and I'll give you five or eight minutes for you to kind of play around with typing all that out, and then we'll regroup.
Exercise 3: Solution
I imagine many of you are probably still doing your copy and pasting of .prototypes and .this references all over the place. We won't spend a lot of time belaboring this because this is mostly just tedium, but I want to point at a couple of things. First of all it was asked, could I do something like NotesManager.prototype equals and do an object literal and just do things like add note and function expressions? And the question was asked, is that possible to avoid some of this typing? The problem that you've done there is you've thrown away the built-in .prototype that was there. And that things that some people want like the .constructor reference, and it might have had a linkage to a different object that you were throwing away. So you want to be careful about sometimes you want to be careful about your .prototypes because sometimes you can end up losing information. This way, generally we do this.prototype stuff all over and over and over again. Yeah? Do you use something like jQuery extend to get around that problem? Most of the class libraries out there do provide some sort of shortcut to this because this is one of the many things that people don't like dealing with. jQuery extend as well as a hundred other utilities that are like it are all about trying to pave over some of these problems. All right, a couple of other things to point out like this show help one. I don't know if anybody got to working on that one. But if you do prototype.showhelp equals function, un-indent, there's a certain special problem that we're going to have to deal with here. All right, so we know inside of here I can do this .help and I can do my document.add event listener. Now this function handler thing that I'm binding to there, I'm going to need to do this reference inside of it, but what's going to happen to this reference if I just pass that function like I'm doing? It's going to be the button in this case, both the DOM API and the jQuery API manually will bind it to the element that fired that. So that's not good. So one solution is to do hard binding to do .bind this on the end of that function so that we force the function reference to be bound to the proper this. That fixes one problem and creates another problem. And the problem that it creates is now, you might now have seen this, but I had a name for this function so that I could unbind it but I can't unbind it by name anymore because the name of the function isn't the actual function that was bound. The function that was bound was that new hard bound function. And we, in fact, don't have a reference to the hard bound function so we have no way to do this remove event list in there properly. Really, this isn't a good solution for doing .bind this. And now typically I would advise against this particular thing but you've probably seen people do stuff like var self equals this. And they can do self here. And then rather than doing this reference we can do a self-reference. Now most of the time I'm going to tell you if you're doing stuff like that you're completely shooting yourself in the foot because you went to all the trouble to do this mechanism style code and then you fell back to lexical style code. The var self equals this is usually a code smell that you're doing something wrong in my opinion. But this is what one tiny exception case with these event handlers where the other way of doing it is just much harder. So it's easier to fall back in this exception case, to a var self equals this. But in general, if you see people doing var self equals this I think that's them not understanding how the mechanisms work. I won't belabor most of these other ones. A lot of these other ones are helpful but let's just point this one out. Down here in our init function, we're definitely going to need to manage all of these references, so I'm going to have this.open_help. I'm going to bind it to this.handleOpenHelp and do a manual bind this. So I have to do that in every one of these which kind of sucks. Does everybody see why I'm doing that? And so on. Now all of these things that were private variables down here we no longer have any private variables so we can get rid of all of those. We no longer have any of this stuff. Assuming we had finished the rest of our conversion then all of this stuff would be just things that were added to that prototype object. And so down here instead of having a notes manager we would say something like var mynotes is equal to new NotesManager. And we'd reference myNotes.loadData and myNotes.init. And lastly because we are keeping track of some state like our notes variable for instance, up in our constructor we're going to want to initialize this.notes is equal to an empty array. Yeah. Which do you like better? The module or the prototype? Unequivocally the module pattern is much more useful in my code than the prototype pattern. But we haven't really seen my favorite pattern yet. We're getting there. Okay, so in addition to all the clunky syntax and all of those things that are falling over, it's just a more complicated mental model to try to pretend that this is classes, and most people solve that problem, as I've said, by using some library to pave over the differences. And any time you have to use some library to pave over the differences you should ask yourself, "Is this too difficult? "Am I doing it the hard way? "Is there a better way?"
Quiz: Prototype Unit
Rather than belaboring some time because we still got async to get into I want to give you as homework Exercise 4. Just real briefly to orient you, it calls for you to create a widget and a button as a parent and child class. So you first use the prototype style and model it as a parent-child class sort of a thing so that you can instantiate buttons on your page like you're instantiating UI widgets. And then it asks you to go back and rethink about it in terms of OLOO style and in terms of delegation. Or rather than having a parent and child you have two peer objects, you have a general widget utility object and you have a more task specific button object. So, that exercise has both the open files for your to work on as well as the fixed version so you end up at the end of that exercise with a nice side by side comparison between class style code and OLOO style code and it lets you further decide which one works for you. There's a question online from James L. Are there any circumstances in which it makes two of your patterns? As in use OLOO to delegate a module or vice versa? Yes, I have done that before. It doesn't happen very often but I have done that before. You have to be a little bit careful because delegation can sometimes be kind of counterproductive to the encapsulation that you're trying to do with modules and vice versa. So, you have to be a little bit careful with it. But I have done delegation between two encapsulated modules before. Is there a generalization you can make about what instances you would do that in? Probably not. (mumbles) Yeah, so you have to start thinking in terms of delegation. Delegation is not a general hammer that you'd use everywhere. I only use it 5% of the time at best. But when I do want to take advantage of the prototype mechanism, I think delegation's a cleaner pattern than classes for it. All right, so without belaboring that stuff, I do encourage you, as your homework, to take a look at Exercise 4, try your hand at it.
Exercise 4 Solution
Solving Callback Problems
So I'm going to give you a couple of real quick things that callbacks try to do. These are things that people have tried to do to solve some of the issues with callbacks. For example, here we have the separate callbacks approach where I pass in a success handler and an error handler, and I expect that one or the other is called. But this is even more implicit trust because I'm trusting that you're only going to call one and not the other. What happens if they call the success and the failure? What happens if they call the failure first and then later they call the success or vice versa? How are we supposed to handle that kind of thing? We're trusting that they're not going to but how would we handle that if they did? What would your program do if both callbacks got called? It would probably break. Okay. So this doesn't really solve inversion of control, it just makes it worst. Then we have, this is commonly called node style but I think that's a really bad name. We should called it error-first style code which is that we pass in a single callback but we get an error function. This error parameter is the first parameter there on line nine. The error parameter will be filled with something truthy if there was an error condition and it will be empty if there wasn't. We find ourselves inside of our functions and this happens a lot in node. We do this if else all over the place. Now we don't have two separate functions but we still have one function, and let me ask you the question. What happens if they pass back both an error object and a success value, how would your code react? You'd probably completely ignore the success value because you'd be checking only for the error object. Again, we really haven't done anything to actually solve the implicit trust issues that have been created by callbacks as a continuation style. Okay, here's my little running example that I'm going to give you calculating the meaning of life and here I'm doing so with nested callbacks, okay? The first callback is I'm calling on line five. This getData function by the way, he just waits a thousand seconds and then gives you your data right back. I'm saying a thousand milliseconds from now I want you to pass me 10 back. I'll add 10 to one and my x will now be 11. Then a thousand milliseconds from now I want you to hand me 30 back and I'll add 30 plus one so I'll have 31. A thousands milliseconds from now I want you to hand me back the string that says meaning of life is equal to the addition of 11 and 31. And you hand me back the answer and I print that out which is the meaning of life is 42, okay? It's these asynchronous steps that I'm doing that might have been AJAX calls or click handlers or any other form of stuff. And it should be evident in this code that if we did not trust that getData function we would have had a massive inversion of control trust issue. There's also lots of problems that we have no error handling here. What happens if halfway through it fails to send us back the value where the program just hangs, you don't have any way of doing it. So you have to construct all kinds of elaborate solutions to these problems that callbacks introduce.
Now let's talk about promises, okay? I know we're running late. If you have to leave, it's okay, I understand. I'm trying to get through it quickly but this is the stuff I'm actually most excited about so I hope this is interesting to you. Promises. Promises, there's a lot of different ways that people explain them. There's a bunch of whole, there's a whole bunch of great blog posts about promises. I encourage you to kind of take a look at some of those. Let me just give you two metaphors for what a promise is all about. Okay, the first one is you walk up to the counter at Burger King or McDonald's or something like that and you're hungry for a Big Mac or a burger, whatever. You have hand the cashier, you order your food, you hand the cashier some money. What usually happens is you don't have your food back right away. What does the cashier hand you instead of your food? Receipts. A receipt and that receipt has an order number on it. So you step back among the other masses of people that are impatiently waiting for their high caloric intake food, and you wait patiently, impatiently for somebody to call out that order number, that magic number on your receipt. And then what do you do once they call out your order number? You walk back up to the counter and you exchange your receipt for the food that you actually wanted. What we have here is we have a transaction that was asynchronously completing. I started the transaction by giving you some money but you couldn't complete the transaction and give me my food yet. You had to give me a promise for some food. You had to give me an IOU for some food and you had to come back at a later time and exchange my order number for my food, okay? That metaphorically is exactly what promises are. I call to a function and I want some end result to happen and that function tells me, sorry I can't finish it yet but I'll hand you back a promise, I'll hand you back a receipt with an order number on it, and at some later time when I get finished with that task I'll let you exchange the order number for the value that you asked for. Does that make sense? All right. The other metaphor that I would give you for promises is what if we could call a function and we weren't sure when that function was going to finish but what if we could subscribe to an event. After calling the function, we have some mechanism by which we could subscribe to an event that lets us know when that function finishes. We might call that a completion event or a continuation event. Kind of like we listen for click events on buttons but this is an event for a function call. That's essentially what promises are. We're calling a function, it doesn't finish yet but it allows us to subscribe to a continuation event. And when we get notified of that event then we proceed. Does that make sense at all? Let's see what that looks like in code. I'm going to use, this is very heretical in the broader standards community because everybody seems to, in the standards community they seem to universally hate the jQuery implementation of promises. Because jQuery had the gall to implement promises in a non-standard way. And jQuery can't go back and change it now because there's 10 million lines of code relying upon it. So, if you talked to anybody in the promises in the standards community and you mention jQuery promises, they will scowl at you and say, "Oh, you shouldn't be using those." I think that's a hogwash, it's nonsense. jQuery has promises, they're useful. It's a pattern rather than an API as far as I'm concerned. I'm going to show you first how jQuery does it. We create these things called deferreds and we pull a promise object off of our deferred. Then we have two separate pieces of code, lines four through six and lines eight through 10 would likely happen in totally different places in our code. Because it's a separation of concerns. Lines four through six are what happens when we want to listen for the event. In this case we want to listen for the done event, the continuation event. So we would return back a promise from some utility and that calling place would be able to listen for the event that was finishing. And then somewhere inside of our code we would, at some later point we would resolve that promise, we would exchange the order number for a Big Mac. When we call resolve on the deferred he will automatically fire the done event for any promises that are listening to him, okay? Here's how we use it for example, creating ourselves a little delay function that I call waitForN. I create my deferred, I return my promise, I set up a timeout for calling d.resolve. That's the typical pattern and you'll see that with all promise implementation, that same kind of pattern, okay? Here's how we use it. We call waitForN a thousand, WaitForN a thousand returns us back a promise so when we call .then, we're listening for the continuation event on that returned promise. Our code says waitForN, wait for a thousand milliseconds then do this stuff. Inside of this function we call waitForN again which generates a whole new promise and when we return a new promise from it, it chains the promises together. This code you can look at it very synchronously. It says wait for a thousand then do this, then do this, then do this, then do this even though it starts and stops asynchronously. You can see that promises give us something similar to what we saw with generators that give us a very synchronous-looking syntax for an asynchronously completing series of tasks. Does everybody follow that? And here's why it's important. Remember when we talked about inversion of control? When I pass a callback into some utility, that utility has control over when my code happens. We've reinverted the control, we've uninverted the control now. We call some utility and rather than giving them our continuation, that utility hands us a promise back and we get to decide what we do with that promise. We listen for the completion of that promise and we decide what to do next. So we've uninverted that inversion of control and brought it back so that we are now in control of the entire completion of our program. Does that make sense? That's why promises are such a powerful solution to callback hell and inversion of control is because they uninvert that inversion of control. Now, promises are actually built in as of ES6 so you can use them natively directly in the language. We're going to get the capital P Promise, so it's built-in, it's already there in Chrome and node, you can play with it if you want. If we construct our promise this should look very similar to what we're doing with jQuery. We construct a promise that we return back and then we call to resolve at some later point. And we call .then and .then and when we return new promises it does exactly the same thing. This is why I don't care that jQuery's API is slightly non-standard because the concept is the same. We still reason about the code in exactly the same way. Kyle, is there part of that chain strategy for each one if you say if I get a timeout and this one I want to do this? Is there like a then otherwise? Yeah, so in native promises, if you pass two functions in, the second function is the error handler for that stage. So there is a way to call reject if you want to fail a promise. There is always an error pack. All right, so we get data. A thousand milliseconds from now it passes along this message, promises have message passing obviously. A thousand milliseconds from now num one will be 10. A thousand milliseconds from now num two will be 30 and a thousand milliseconds later answer will be the string, meaning of life 42.
The last thing I want to do, I know we're overtime but it's the last thing I want to do, is I want to show you a library that I wrote called asynquence. I talked about it at the beginning of the day. Because there are some really great things about promises but there are also some really not so great things about them. In that they don't go far enough with some of their abstractions. And I think the future of promises is that most people will continue to use promises with abstraction libraries on top of them. Promises are a really, really low level mechanism and in some of your most simple use cases, like for instance this one, it's not too much difficult to use. But when you start to get into really complex, real world code, it starts to get really tedious to create these promises all over the place, and to figure out how to manually chain them together. When you start thinking about more complex tasks like what if it's not this then this then this? What if it's this and all of these things and then this thing and these two things, and then this thing except go back and start. What if you start having those sorts of flow control? Promises start to get really tedious. And that's why there are promises abstraction libraries to try to help some of that tedium. Try to take care of some of that tedium for you. There's a number of ones out there, there's async and queue and several other ones. I happen to have one that I think does a pretty good job of making things simple and it's called asynquence. Asynquence stands for asynchronous sequences. So sequences are automatically chained promises. If you'll notice in our previous promise examples, you had to manually create a new promise and return it from each step to keep the chain going. Whereas in most cases, you could just assume that somebody wants to keep going because there's always another step in your task. Most of the real world times it's a lot more than just one step. Asynquence assumes it and it automatically creates and chains promises for you. And here's a simple distillation of the code. You create yourself a sequence by calling the constructor the ASQ. You have your then steps because it's just a series of then steps but you'll notice you don't have to create promises here. It automatically creates a promise for you and it passes in the completion trigger. So it assumes that you're going to want to go on and it creates the promise for you, and you just need to make sure to call the trigger at the proper time. You don't have to do any of that work of creating those promises for these normal steps, it's created them underneath the covers. We also have the ability to do the other primitive and asynchronous flow control is what we call gates. Sequences are this then this then this then this. A gate says two or more things are going to happen at the same time and I don't care when they finish and in what order. I just want to wait for all of them to finish before I move on. It waits for everybody to get to the gates, it's an old school terminology from processes and fork and stuff. Asynquence lets you split from a sequence into a gate step back into a sequence. And you can express any, you know, arbitrary complexity of flow control using the asynquence API. For example, here's our little meaning of life example. I have rather than returning a promise I return it back a sequence. Here I can do what's called a waterfall which does these things in order but it keeps track of the messages. So it calculates 10, sends that along. It calculates 30 and it sends that along. 10 and 30 come in. We then calculate the meaning of life as x and y and we pass that one along. You can do the same sort of message passing, just has a lot less work because you don't have to create the promises every time. Finally, it was mentioned earlier what about generators and promises? I think that generators plus promises are actually the most popular pattern that we're going to move towards for asynchronous flow control. And I've got an API built into asynquence that handles this natively, it's called runner. It allows you to pass in a generator and yield out sequences or promises. It automatically, this little runner utility. When you yield that out, it will receive that promise, listen for the promise to complete before it starts back up the generator. It will automatically continue to the end for you. We would yield out a promise or a sequence, forget data and when that guy sends us the value back in then 10's going to come back in. Then we yield out another promise, a second later we get 30 back. We finally yield out a third promise for the string and a second later we get that answer back, and we can return that answer which then gets passed along, along the sequence chain. Finally, I just want to show you something cool because I just was starting to experiment with this. I actually think this is some unexplored territory here. The idea of taking two or more generators and interleaving their operation. Runner actually allows you to specify two or more generators and what will happen is the first one, the first time it yields, it will actually message past to this generator. And when this generator yields it will pass to the next one in a round-robin fashion. You can actually have these generators running at the same time starting and stopping and message passing to each other, which is going to lead to all kinds of powerful stuff. This is called CSP, Communicating Serial Processes. It's a model, it's a really advanced model for continuation style.
Quiz: Async Patterns
The bottom line takeaway is you can use promises natively or you can use any library that you're happy with. I think asynquence does a pretty good job of making it easy to deal with promises without all of the complexity, but you know, feel free to use it or not. Finishing up, what is callback hell? What is it really? Giving over control of your program. Exactly. It's inversion of control. It's handing your control of the rest of your program over to some of the utility and all of the trust that that involves, and all of the problems that it can introduce. The lack of error handling, the lack of retries, the lack of knowing if it's going to do it too many times or not enough or all of that. How do you pause a generator? What was the mechanism we used for pausing a generator? Yield. Yield. Yield and how do you resume it? Next. Next. .Next on the (mumbles). What is a Promise? Something that will eventually either be rejected or accepted. Yeah. Or may notify. It's a future value is what it's usually called. It's a promise for a future value. It's a continuation event. It's a transaction at Burger King, however you want to think about metaphorically. And how does it solve the inversion of control issue? Gives you back the ability to decide whether your code's going to be running or your continuation's going to be. That's right. Instead of passing my continuation in, I receive a promise back. So it uninverts that inversion of control. And it puts me back in control of deciding what step two looks like. Finally, how is that we could, what was the key characteristic that allowed us to combine generators and promises for async flow control? Remember, the generator's yielding out promises and the promise when it completes automatically restarting the generator. So the key characteristic was call a generator, have it yield out a promise and when that promise finishes, have the promise restart the generator. And most of the async flow libraries out there including asynquence have a utility for that. Some of them call it spawn, mine is called runner but it automatically listens for yielded out promises and it restarts the generator when they complete.
Unfortunately I'm going to have to assign you yet another piece. I'd hope that we could get a chance to go through this. There is this exercise five, this is a brand new one that I just created so you guys are the first ones to see this. But I really encourage you to spend some time tonight playing with it because it's actually kind of fun. What the task is it requires you to, it's a fake AJAX load but it requires you to load up three separate files in parallel. You're going to try to get the contents of those files at the same time. You want to do it in parallel but it wants to make sure that you print them out in the proper order. And that's a deceptively simple sounding task, that's actually a little bit complex to figure out. So I've implemented it using callbacks and you can see how difficult it is to manage the state with nested callbacks. And then the challenge for you is using whatever promises or generator syntax you like, whatever library. I've shown you how to do it with asynquence and I've also shown you how to do it. There's files in there to do it with asynquence and also with native promises. But whichever format you want, I want you to try to solve that task. How could I load up three separate things but make sure they print in the proper order? Because that, you know, that is a real world sort of asynchronous task that we face in our code. I wish we had time to kind of talk through it. If you guys have questions about it, please feel free to email. We could maybe take 10 minutes tomorrow morning. Yeah, we'll probably go over the exercise. If you guys get a chance tonight, we'll probably go over the exercise, that's a good idea. Speaking of tomorrow, tomorrow is going to look somewhat different than today in terms of the style of the way the class goes. So just to prepare you guys as you're packing up. The style for tomorrow is going to be a lot more code and a lot less lecture, and that may or may not be what you're hoping for. But there will be a lot less slides, a lot less of me sort of standing for long periods of time and talking, and we're going to do a lot more tinkering at the codes. So you're going to write real world node code and the goal is to take what we learn today as a foundation of confidence so that we can write code in our node JS processes without worrying about how these mechanisms work. We'll revisit asynchronicity and we'll revisit a whole bunch of these other things as we deal with them, okay? I appreciate everybody's time today and all your great questions and participation. I look forward to tomorrow, to the second day. You have any question let me know but I appreciate it very much.
Exercise 5 Solution
Kyle is a freelance developer based in Austin, TX. He runs several open-source projects (such as LabJS), writes books, and speaks at meetups and conferences.
Released10 Apr 2015