├── .editorconfig
├── .github
└── ISSUE_TEMPLATE
│ ├── content-question.md
│ ├── foreign-translation-request.md
│ ├── report-technical-mistake.md
│ └── textual-grammar-typo.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── PULL_REQUEST_TEMPLATE.md
├── README.md
├── es-next-beyond
├── README.md
├── apA.md
├── ch1.md
├── ch2.md
├── ch3.md
├── ch4.md
├── ch5.md
├── ch6.md
├── ch7.md
├── ch8.md
├── fig1.png
├── foreword.md
└── toc.md
├── get-started
├── README.md
├── apA.md
├── apB.md
├── ch1.md
├── ch2.md
├── ch3.md
├── ch4.md
├── fig1.png
├── fig2.png
├── fig3.png
├── fig4.png
├── fig5.png
├── fig6.png
├── foreword.md
└── toc.md
├── getting-started
└── README.md
├── objects-classes
├── README.md
├── apA.md
├── ch1.md
├── ch2.md
├── ch3.md
├── ch4.md
├── ch5.md
├── ch6.md
├── fig1.png
├── fig2.png
├── fig3.png
├── fig4.png
├── fig5.png
├── fig6.png
├── foreword.md
└── toc.md
├── preface.md
├── scope-closures
├── README.md
├── apA.md
├── apB.md
├── ch1.md
├── ch2.md
├── ch3.md
├── ch4.md
├── ch5.md
├── ch6.md
├── fig1.png
├── fig2.png
├── foreword.md
└── toc.md
├── sync-async
├── README.md
├── apA.md
├── apB.md
├── ch1.md
├── ch2.md
├── ch3.md
├── ch4.md
├── ch5.md
├── ch6.md
├── foreword.md
└── toc.md
└── types-grammar
├── README.md
├── apA.md
├── ch1.md
├── ch2.md
├── ch3.md
├── ch4.md
├── ch5.md
├── fig1.png
├── foreword.md
└── toc.md
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = tab
8 | indent_size = 4
9 |
10 | [*.md]
11 | indent_style = space
12 | indent_size = 4
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/content-question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Content Question
3 | about: Ask a question about something you read in the books?
4 | labels:
5 |
6 | ---
7 |
8 | **Yes, I promise I've read the [Contributions Guidelines](https://github.com/getify/You-Dont-Know-JS/blob/master/CONTRIBUTING.md)** (please feel free to remove this line).
9 |
10 | ----
11 |
12 | **Please type "I already searched for this issue":**
13 |
14 | **Edition:** (1st or 2nd)
15 |
16 | **Book Title:**
17 |
18 | **Chapter:**
19 |
20 | **Section Title:**
21 |
22 | **Question:**
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/foreign-translation-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Foreign Translation Request
3 | about: Want to request a translation into a foreign language?
4 | labels:
5 |
6 | ---
7 |
8 | Foreign translations are appreciated. However, any such requests should wait until the 2nd edition is complete.
9 |
10 | **I will not be accepting any new foreign translations for previous editions.**
11 |
12 | ----
13 |
14 | Please check these issues first:
15 |
16 | * https://github.com/getify/You-Dont-Know-JS/issues?utf8=%E2%9C%93&q=label%3A%22foreign+language+translations%22+
17 | * https://github.com/getify/You-Dont-Know-JS/issues/9
18 | * https://github.com/getify/You-Dont-Know-JS/issues/900
19 | * https://github.com/getify/You-Dont-Know-JS/issues/1378
20 |
21 | To summarize, the steps for a foreign language translation are:
22 |
23 | 1. Fork this repo
24 | 2. Make your own translation entirely in your fork, preferably of all six books, but at a minimum of one whole book
25 | 3. File an issue asking for a branch to be made on our main repo, named for that [language's ISO code](http://www.lingoes.net/en/translator/langcode.htm)
26 | 4. Once the branch is created, you can PR to merge your translated work in
27 | 5. Once the merge is complete, I will promote you to a repository maintainer so you can manage any further translation maintenance work on your own branch of this repo
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/report-technical-mistake.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Report Technical Mistake
3 | about: Help us fix a mistake in the code.
4 | labels:
5 |
6 | ---
7 |
8 | **Yes, I promise I've read the [Contributions Guidelines](https://github.com/getify/You-Dont-Know-JS/blob/master/CONTRIBUTING.md)** (please feel free to remove this line).
9 |
10 | ----
11 |
12 | **Please type "I already searched for this issue":**
13 |
14 | **Edition:** (1st or 2nd)
15 |
16 | **Book Title:**
17 |
18 | **Chapter:**
19 |
20 | **Section Title:**
21 |
22 | **Problem:**
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/textual-grammar-typo.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Textual/Grammar Typo
3 | about: Help us correct a spelling or grammar error in the text.
4 | labels:
5 |
6 | ---
7 |
8 | **Yes, I promise I've read the [Contributions Guidelines](https://github.com/getify/You-Dont-Know-JS/blob/master/CONTRIBUTING.md)** (please feel free to remove this line).
9 |
10 | ----
11 |
12 | **Please type "I already searched for this issue":**
13 |
14 | **Edition:** (1st or 2nd)
15 |
16 | **Book Title:**
17 |
18 | **Chapter:**
19 |
20 | **Section Title:**
21 |
22 | **Problem:**
23 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Please feel free to contribute to the quality of this content by submitting PR's for improvements to code snippets, explanations, etc. If there's any doubt or if you think that a word/phrase is used confusingly, **before submitting a PR, open an issue to ask about it.**
4 |
5 | However, if you choose to contribute content (not just typo corrections) to this repo, you agree that you're giving me a non-exclusive license to use that content for the book, as I (and my publisher) deem appropriate. You probably guessed that already, but I just wanted to be extra clear on that.
6 |
7 | ## Reading Experience (Chapter/Section links, etc)
8 |
9 | I understand that reading one long .md file, with no relative cross links to other sections/etc, is not the preferred reading experience for most of you. As such, it's totally reasonable to want to file an issue/PR to add those kinds of features.
10 |
11 | This topic has been brought up many times, and I've considered it. For now, I **do not** accept these kinds of changes into the repo.
12 |
13 | The main purpose of my book repos is to track and manage the content for the purposes of publication (paid-for ebooks and print books). I do this in the open because I also care about providing free and early access to the content, to make sure there is no paywall barrier to learning.
14 |
15 | As such, this repo **is not optimized for your reading experience.**
16 |
17 | The intended **best reading experience** are the published books (either ebook or print), so I encourage you to purchase them for that purpose.
18 |
19 | ## Editions
20 |
21 | The current edition (*work in progress*) of the books is the 2nd edition. While these 2nd edition books are in progress, contributions are OK, but **there's no need to jump the gun early** while I'm still working on stuff. That's what "work in progress" means. :)
22 |
23 | | NOTE: |
24 | | :--- |
25 | | **ALSO**, please note that while a book is in progress, the previous 1st edition text may be included at the bottom of each file, and marked as such. Please ignore this text, as contributions there are not relevant. |
26 |
27 | I **am not accepting any contributions** for 1st edition books.
28 |
29 | ## Typos?
30 |
31 | Please don't worry about minor text typos. These will almost certainly be caught during the editing process.
32 |
33 | If you're going to submit a PR for typo fixes, please be measured in doing so by collecting several small changes into a single PR (in separate commits). Or, **just don't even worry about them for now,** because we'll get to them later. I promise.
34 |
35 | ## Search First!
36 |
37 | Also, if you have any questions or concerns, please make sure to search the issues (both open and closed!) first, to keep the churn of issues to a minimum. I want to keep my focus on writing these books as much as possible.
38 |
--------------------------------------------------------------------------------
/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **Yes, I promise I've read the [Contributions Guidelines](https://github.com/getify/You-Dont-Know-JS/blob/master/CONTRIBUTING.md)** (please feel free to remove this line).
2 |
3 | Specifically quoting these guidelines regarding typos:
4 |
5 | > Typos?
6 | >
7 | > Please don't worry about minor text typos. These will almost certainly be caught during the editing process.
8 | >
9 | > If you're going to submit a PR for typo fixes, please be measured in doing so by collecting several small changes into a single PR (in separate commits). Or, **just don't even worry about them for now,** because we'll get to them later. I promise.
10 |
11 | ----
12 |
13 | **Please type "I already searched for this issue":**
14 |
15 | **Edition:** (pull requests not accepted for previous editions)
16 |
17 | **Book Title:**
18 |
19 | **Chapter:**
20 |
21 | **Section Title:**
22 |
23 | **Topic:**
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet (book series) - 2nd Edition
2 |
3 | This is a series of books diving deep into the core mechanisms of the JavaScript language.
4 |
5 | **To read more about the motivations and perspective behind this book series, check out the [Preface](preface.md).**
6 |
7 | ## Premier Sponsor
8 |
9 | This edition of the **YDKJS** book series is exclusively sponsored by [Frontend Masters](https://frontendmasters.com).
10 |
11 | Frontend Masters is the gold standard for top-of-the-line expert training material in frontend-oriented software development. With over 150 courses on all things frontend, this should be your first and only stop for quality video training on HTML, CSS, JS, and related technologies.
12 |
13 | ----
14 |
15 | I teach [all my workshops](https://frontendmasters.com/kyle-simpson) exclusively through Frontend Masters. If you like this book content, please check out my video training courses.
16 |
17 | I want to extend a warm and deep thanks to Marc Grabanski and the entire Frontend Masters team, not only for their excellent work with the video training platform, but for their unwavering support of me and of the "You Don't Know JS" books!
18 |
19 | ----
20 |
21 | ## Titles
22 |
23 | I recommend reading the books in this order:
24 |
25 | * [Get Started](get-started/README.md)
26 | * [Scope & Closures](scope-closures/README.md)
27 | * [Objects & Classes](objects-classes/README.md)
28 | * [Types & Grammar](types-grammar/README.md)
29 | * [Sync & Async](sync-async/README.md)
30 | * [ES.Next & Beyond](es-next-beyond/README.md)
31 |
32 | ## Publishing
33 |
34 | As always, you'll be able to read these books online here entirely for free.
35 |
36 | This edition of the books is being self-published through [GetiPub](https://geti.pub) publishing. The published books will be made available for sale through normal book retail sources.
37 |
38 | If you'd like to contribute financially towards the effort (or any of my other OSS efforts) aside from purchasing the published books, please consider these options:
39 |
40 | * [Github Sponsorship](https://github.com/users/getify/sponsorship)
41 | * [Patreon](https://www.patreon.com/getify)
42 | * [Paypal](https://www.paypal.me/getify)
43 |
44 | ## Contributions
45 |
46 | Please feel free to contribute to the quality of this content by submitting PRs for improvements to code snippets, explanations, etc. While typo fixes are welcomed, they will likely be caught through normal editing/publishing processes, **so please don't worry about them right now.**
47 |
48 | Any contributions you make to this effort **are of course greatly appreciated**.
49 |
50 | But **PLEASE** read the [Contributions Guidelines](CONTRIBUTING.md) carefully before submitting a PR.
51 |
52 | ## License & Copyright
53 |
54 | The materials herein are all © 2019-2020 Kyle Simpson.
55 |
56 | 
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 4.0 Unported License.
57 |
--------------------------------------------------------------------------------
/es-next-beyond/README.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: ES.Next & Beyond - 2nd Edition
2 |
3 | | NOTE: |
4 | | :--- |
5 | | Work in progress |
6 |
7 | [Table of Contents](toc.md)
8 |
9 | * [Foreword](foreword.md) (by TBA)
10 | * [Preface](../preface.md)
11 | * [Chapter 1: ES? Now & Future](ch1.md)
12 | * [Chapter 2: Syntax](ch2.md)
13 | * [Chapter 3: Organization](ch3.md)
14 | * [Chapter 4: Async Flow Control](ch4.md)
15 | * [Chapter 5: Collections](ch5.md)
16 | * [Chapter 6: API Additions](ch6.md)
17 | * [Chapter 7: Meta Programming](ch7.md)
18 | * [Chapter 8: Beyond ES6](ch8.md)
19 | * [Appendix A: TODO](apA.md)
20 |
--------------------------------------------------------------------------------
/es-next-beyond/apA.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: ES.Next & Beyond - 2nd Edition
2 | # Appendix A: TODO
3 |
4 | | NOTE: |
5 | | :--- |
6 | | Work in progress |
7 |
--------------------------------------------------------------------------------
/es-next-beyond/ch1.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: ES.Next & Beyond - 2nd Edition
2 | # Chapter 1: ES? Now & Future
3 |
4 | | NOTE: |
5 | | :--- |
6 | | Work in progress |
7 |
8 | .
9 |
10 | .
11 |
12 | .
13 |
14 | .
15 |
16 | .
17 |
18 | .
19 |
20 | .
21 |
22 | ----
23 |
24 | | NOTE: |
25 | | :--- |
26 | | Everything below here is previous text from 1st edition, and is only here for reference while 2nd edition work is underway. **Please ignore this stuff.** |
27 |
28 | Before you dive into this book, you should have a solid working proficiency over JavaScript up to the most recent standard (at the time of this writing), which is commonly called *ES5* (technically ES 5.1). Here, we plan to talk squarely about the upcoming *ES6*, as well as cast our vision beyond to understand how JS will evolve moving forward.
29 |
30 | If you are still looking for confidence with JavaScript, I highly recommend you read the other titles in this series first:
31 |
32 | * *Up & Going*: Are you new to programming and JS? This is the roadmap you need to consult as you start your learning journey.
33 | * *Scope & Closures*: Did you know that JS lexical scope is based on compiler (not interpreter!) semantics? Can you explain how closures are a direct result of lexical scope and functions as values?
34 | * *this & Object Prototypes*: Can you recite the four simple rules for how `this` is bound? Have you been muddling through fake "classes" in JS instead of adopting the simpler "behavior delegation" design pattern? Ever heard of *objects linked to other objects* (OLOO)?
35 | * *Types & Grammar*: Do you know the built-in types in JS, and more importantly, do you know how to properly and safely use coercion between types? How comfortable are you with the nuances of JS grammar/syntax?
36 | * *Async & Performance*: Are you still using callbacks to manage your asynchrony? Can you explain what a promise is and why/how it solves "callback hell"? Do you know how to use generators to improve the legibility of async code? What exactly constitutes mature optimization of JS programs and individual operations?
37 |
38 | If you've already read all those titles and you feel pretty comfortable with the topics they cover, it's time we dive into the evolution of JS to explore all the changes coming not only soon but farther over the horizon.
39 |
40 | Unlike ES5, ES6 is not just a modest set of new APIs added to the language. It incorporates a whole slew of new syntactic forms, some of which may take quite a bit of getting used to. There's also a variety of new organization forms and new API helpers for various data types.
41 |
42 | ES6 is a radical jump forward for the language. Even if you think you know JS in ES5, ES6 is full of new stuff you *don't know yet*, so get ready! This book explores all the major themes of ES6 that you need to get up to speed on, and even gives you a glimpse of future features coming down the track that you should be aware of.
43 |
44 | **Warning:** All code in this book assumes an ES6+ environment. At the time of this writing, ES6 support varies quite a bit in browsers and JS environments (like Node.js), so your mileage may vary.
45 |
46 | ## Versioning
47 |
48 | The JavaScript standard is referred to officially as "ECMAScript" (abbreviated "ES"), and up until just recently has been versioned entirely by ordinal number (i.e., "5" for "5th edition").
49 |
50 | The earliest versions, ES1 and ES2, were not widely known or implemented. ES3 was the first widespread baseline for JavaScript, and constitutes the JavaScript standard for browsers like IE6-8 and older Android 2.x mobile browsers. For political reasons beyond what we'll cover here, the ill-fated ES4 never came about.
51 |
52 | In 2009, ES5 was officially finalized (later ES5.1 in 2011), and settled as the widespread standard for JS for the modern revolution and explosion of browsers, such as Firefox, Chrome, Opera, Safari, and many others.
53 |
54 | Leading up to the expected *next* version of JS (slipped from 2013 to 2014 and then 2015), the obvious and common label in discourse has been ES6.
55 |
56 | However, late into the ES6 specification timeline, suggestions have surfaced that versioning may in the future switch to a year-based schema, such as ES2016 (aka ES7) to refer to whatever version of the specification is finalized before the end of 2016. Some disagree, but ES6 will likely maintain its dominant mindshare over the late-change substitute ES2015. However, ES2016 may in fact signal the new year-based schema.
57 |
58 | It has also been observed that the pace of JS evolution is much faster even than single-year versioning. As soon as an idea begins to progress through standards discussions, browsers start prototyping the feature, and early adopters start experimenting with the code.
59 |
60 | Usually well before there's an official stamp of approval, a feature is de facto standardized by virtue of this early engine/tooling prototyping. So it's also valid to consider the future of JS versioning to be per-feature rather than per-arbitrary-collection-of-major-features (as it is now) or even per-year (as it may become).
61 |
62 | The takeaway is that the version labels stop being as important, and JavaScript starts to be seen more as an evergreen, living standard. The best way to cope with this is to stop thinking about your code base as being "ES6-based," for instance, and instead consider it feature by feature for support.
63 |
64 | ## Transpiling
65 |
66 | Made even worse by the rapid evolution of features, a problem arises for JS developers who at once may both strongly desire to use new features while at the same time being slapped with the reality that their sites/apps may need to support older browsers without such support.
67 |
68 | The way ES5 appears to have played out in the broader industry, the typical mindset was that code bases waited to adopt ES5 until most if not all pre-ES5 environments had fallen out of their support spectrum. As a result, many are just recently (at the time of this writing) starting to adopt things like `strict` mode, which landed in ES5 over five years ago.
69 |
70 | It's widely considered to be a harmful approach for the future of the JS ecosystem to wait around and trail the specification by so many years. All those responsible for evolving the language desire for developers to begin basing their code on the new features and patterns as soon as they stabilize in specification form and browsers have a chance to implement them.
71 |
72 | So how do we resolve this seeming contradiction? The answer is tooling, specifically a technique called *transpiling* (transformation + compiling). Roughly, the idea is to use a special tool to transform your ES6 code into equivalent (or close!) matches that work in ES5 environments.
73 |
74 | For example, consider shorthand property definitions (see "Object Literal Extensions" in Chapter 2). Here's the ES6 form:
75 |
76 | ```js
77 | var foo = [1,2,3];
78 |
79 | var obj = {
80 | foo // means `foo: foo`
81 | };
82 |
83 | obj.foo; // [1,2,3]
84 | ```
85 |
86 | But (roughly) here's how that transpiles:
87 |
88 | ```js
89 | var foo = [1,2,3];
90 |
91 | var obj = {
92 | foo: foo
93 | };
94 |
95 | obj.foo; // [1,2,3]
96 | ```
97 |
98 | This is a minor but pleasant transformation that lets us shorten the `foo: foo` in an object literal declaration to just `foo`, if the names are the same.
99 |
100 | Transpilers perform these transformations for you, usually in a build workflow step similar to how you perform linting, minification, and other similar operations.
101 |
102 | ### Shims/Polyfills
103 |
104 | Not all new ES6 features need a transpiler. Polyfills (aka shims) are a pattern for defining equivalent behavior from a newer environment into an older environment, when possible. Syntax cannot be polyfilled, but APIs often can be.
105 |
106 | For example, `Object.is(..)` is a new utility for checking strict equality of two values but without the nuanced exceptions that `===` has for `NaN` and `-0` values. The polyfill for `Object.is(..)` is pretty easy:
107 |
108 | ```js
109 | if (!Object.is) {
110 | Object.is = function(v1, v2) {
111 | // test for `-0`
112 | if (v1 === 0 && v2 === 0) {
113 | return 1 / v1 === 1 / v2;
114 | }
115 | // test for `NaN`
116 | if (v1 !== v1) {
117 | return v2 !== v2;
118 | }
119 | // everything else
120 | return v1 === v2;
121 | };
122 | }
123 | ```
124 |
125 | **Tip:** Pay attention to the outer `if` statement guard wrapped around the polyfill. This is an important detail, which means the snippet only defines its fallback behavior for older environments where the API in question isn't already defined; it would be very rare that you'd want to overwrite an existing API.
126 |
127 | There's a great collection of ES6 shims called "ES6 Shim" (https://github.com/paulmillr/es6-shim/) that you should definitely adopt as a standard part of any new JS project!
128 |
129 | It is assumed that JS will continue to evolve constantly, with browsers rolling out support for features continually rather than in large chunks. So the best strategy for keeping updated as it evolves is to just introduce polyfill shims into your code base, and a transpiler step into your build workflow, right now and get used to that new reality.
130 |
131 | If you decide to keep the status quo and just wait around for all browsers without a feature supported to go away before you start using the feature, you're always going to be way behind. You'll sadly be missing out on all the innovations designed to make writing JavaScript more effective, efficient, and robust.
132 |
133 | ## Review
134 |
135 | ES6 (some may try to call it ES2015) is just landing as of the time of this writing, and it has lots of new stuff you need to learn!
136 |
137 | But it's even more important to shift your mindset to align with the new way that JavaScript is going to evolve. It's not just waiting around for years for some official document to get a vote of approval, as many have done in the past.
138 |
139 | Now, JavaScript features land in browsers as they become ready, and it's up to you whether you'll get on the train early or whether you'll be playing costly catch-up games years from now.
140 |
141 | Whatever labels that future JavaScript adopts, it's going to move a lot quicker than it ever has before. Transpilers and shims/polyfills are important tools to keep you on the forefront of where the language is headed.
142 |
143 | If there's any narrative important to understand about the new reality for JavaScript, it's that all JS developers are strongly implored to move from the trailing edge of the curve to the leading edge. And learning ES6 is where that all starts!
144 |
--------------------------------------------------------------------------------
/es-next-beyond/ch4.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: ES.Next & Beyond - 2nd Edition
2 | # Chapter 4: Async Flow Control
3 |
4 | | NOTE: |
5 | | :--- |
6 | | Work in progress |
7 |
8 | .
9 |
10 | .
11 |
12 | .
13 |
14 | .
15 |
16 | .
17 |
18 | .
19 |
20 | .
21 |
22 | ----
23 |
24 | | NOTE: |
25 | | :--- |
26 | | Everything below here is previous text from 1st edition, and is only here for reference while 2nd edition work is underway. **Please ignore this stuff.** |
27 |
28 | It's no secret if you've written any significant amount of JavaScript that asynchronous programming is a required skill. The primary mechanism for managing asynchrony has been the function callback.
29 |
30 | However, ES6 adds a new feature that helps address significant shortcomings in the callbacks-only approach to async: *Promises*. In addition, we can revisit generators (from the previous chapter) and see a pattern for combining the two that's a major step forward in async flow control programming in JavaScript.
31 |
32 | ## Promises
33 |
34 | Let's clear up some misconceptions: Promises are not about replacing callbacks. Promises provide a trustable intermediary -- that is, between your calling code and the async code that will perform the task -- to manage callbacks.
35 |
36 | Another way of thinking about a Promise is as an event listener, on which you can register to listen for an event that lets you know when a task has completed. It's an event that will only ever fire once, but it can be thought of as an event nonetheless.
37 |
38 | Promises can be chained together, which can sequence a series of asychronously completing steps. Together with higher-level abstractions like the `all(..)` method (in classic terms, a "gate") and the `race(..)` method (in classic terms, a "latch"), promise chains provide a mechanism for async flow control.
39 |
40 | Yet another way of conceptualizing a Promise is that it's a *future value*, a time-independent container wrapped around a value. This container can be reasoned about identically whether the underlying value is final or not. Observing the resolution of a Promise extracts this value once available. In other words, a Promise is said to be the async version of a sync function's return value.
41 |
42 | A Promise can only have one of two possible resolution outcomes: fulfilled or rejected, with an optional single value. If a Promise is fulfilled, the final value is called a fulfillment. If it's rejected, the final value is called a reason (as in, a "reason for rejection"). Promises can only be resolved (fulfillment or rejection) *once*. Any further attempts to fulfill or reject are simply ignored. Thus, once a Promise is resolved, it's an immutable value that cannot be changed.
43 |
44 | Clearly, there are several different ways to think about what a Promise is. No single perspective is fully sufficient, but each provides a separate aspect of the whole. The big takeaway is that they offer a significant improvement over callbacks-only async, namely that they provide order, predictability, and trustability.
45 |
46 | ### Making and Using Promises
47 |
48 | To construct a promise instance, use the `Promise(..)` constructor:
49 |
50 | ```js
51 | var p = new Promise( function pr(resolve,reject){
52 | // ..
53 | } );
54 | ```
55 |
56 | The `Promise(..)` constructor takes a single function (`pr(..)`), which is called immediately and receives two control functions as arguments, usually named `resolve(..)` and `reject(..)`. They are used as:
57 |
58 | * If you call `reject(..)`, the promise is rejected, and if any value is passed to `reject(..)`, it is set as the reason for rejection.
59 | * If you call `resolve(..)` with no value, or any non-promise value, the promise is fulfilled.
60 | * If you call `resolve(..)` and pass another promise, this promise simply adopts the state -- whether immediate or eventual -- of the passed promise (either fulfillment or rejection).
61 |
62 | Here's how you'd typically use a promise to refactor a callback-reliant function call. If you start out with an `ajax(..)` utility that expects to be able to call an error-first style callback:
63 |
64 | ```js
65 | function ajax(url,cb) {
66 | // make request, eventually call `cb(..)`
67 | }
68 |
69 | // ..
70 |
71 | ajax( "http://some.url.1", function handler(err,contents){
72 | if (err) {
73 | // handle ajax error
74 | }
75 | else {
76 | // handle `contents` success
77 | }
78 | } );
79 | ```
80 |
81 | You can convert it to:
82 |
83 | ```js
84 | function ajax(url) {
85 | return new Promise( function pr(resolve,reject){
86 | // make request, eventually call
87 | // either `resolve(..)` or `reject(..)`
88 | } );
89 | }
90 |
91 | // ..
92 |
93 | ajax( "http://some.url.1" )
94 | .then(
95 | function fulfilled(contents){
96 | // handle `contents` success
97 | },
98 | function rejected(reason){
99 | // handle ajax error reason
100 | }
101 | );
102 | ```
103 |
104 | Promises have a `then(..)` method that accepts one or two callback functions. The first function (if present) is treated as the handler to call if the promise is fulfilled successfully. The second function (if present) is treated as the handler to call if the promise is rejected explicitly, or if any error/exception is caught during resolution.
105 |
106 | If one of the arguments is omitted or otherwise not a valid function -- typically you'll use `null` instead -- a default placeholder equivalent is used. The default success callback passes its fulfillment value along and the default error callback propagates its rejection reason along.
107 |
108 | The shorthand for calling `then(null,handleRejection)` is `catch(handleRejection)`.
109 |
110 | Both `then(..)` and `catch(..)` automatically construct and return another promise instance, which is wired to receive the resolution from whatever the return value is from the original promise's fulfillment or rejection handler (whichever is actually called). Consider:
111 |
112 | ```js
113 | ajax( "http://some.url.1" )
114 | .then(
115 | function fulfilled(contents){
116 | return contents.toUpperCase();
117 | },
118 | function rejected(reason){
119 | return "DEFAULT VALUE";
120 | }
121 | )
122 | .then( function fulfilled(data){
123 | // handle data from original promise's
124 | // handlers
125 | } );
126 | ```
127 |
128 | In this snippet, we're returning an immediate value from either `fulfilled(..)` or `rejected(..)`, which then is received on the next event turn in the second `then(..)`'s `fulfilled(..)`. If we instead return a new promise, that new promise is subsumed and adopted as the resolution:
129 |
130 | ```js
131 | ajax( "http://some.url.1" )
132 | .then(
133 | function fulfilled(contents){
134 | return ajax(
135 | "http://some.url.2?v=" + contents
136 | );
137 | },
138 | function rejected(reason){
139 | return ajax(
140 | "http://backup.url.3?err=" + reason
141 | );
142 | }
143 | )
144 | .then( function fulfilled(contents){
145 | // `contents` comes from the subsequent
146 | // `ajax(..)` call, whichever it was
147 | } );
148 | ```
149 |
150 | It's important to note that an exception (or rejected promise) in the first `fulfilled(..)` will *not* result in the first `rejected(..)` being called, as that handler only responds to the resolution of the first original promise. Instead, the second promise, which the second `then(..)` is called against, receives that rejection.
151 |
152 | In this previous snippet, we are not listening for that rejection, which means it will be silently held onto for future observation. If you never observe it by calling a `then(..)` or `catch(..)`, then it will go unhandled. Some browser developer consoles may detect these unhandled rejections and report them, but this is not reliably guaranteed; you should always observe promise rejections.
153 |
154 | **Note:** This was just a brief overview of Promise theory and behavior. For a much more in-depth exploration, see Chapter 3 of the *Async & Performance* title of this series.
155 |
156 | ### Thenables
157 |
158 | Promises are genuine instances of the `Promise(..)` constructor. However, there are promise-like objects called *thenables* that generally can interoperate with the Promise mechanisms.
159 |
160 | Any object (or function) with a `then(..)` function on it is assumed to be a thenable. Any place where the Promise mechanisms can accept and adopt the state of a genuine promise, they can also handle a thenable.
161 |
162 | Thenables are basically a general label for any promise-like value that may have been created by some other system than the actual `Promise(..)` constructor. In that perspective, a thenable is generally less trustable than a genuine Promise. Consider this misbehaving thenable, for example:
163 |
164 | ```js
165 | var th = {
166 | then: function thener( fulfilled ) {
167 | // call `fulfilled(..)` once every 100ms forever
168 | setInterval( fulfilled, 100 );
169 | }
170 | };
171 | ```
172 |
173 | If you received that thenable and chained it with `th.then(..)`, you'd likely be surprised that your fulfillment handler is called repeatedly, when normal Promises are supposed to only ever be resolved once.
174 |
175 | Generally, if you're receiving what purports to be a promise or thenable back from some other system, you shouldn't just trust it blindly. In the next section, we'll see a utility included with ES6 Promises that helps address this trust concern.
176 |
177 | But to further understand the perils of this issue, consider that *any* object in *any* piece of code that's ever been defined to have a method on it called `then(..)` can be potentially confused as a thenable -- if used with Promises, of course -- regardless of if that thing was ever intended to even remotely be related to Promise-style async coding.
178 |
179 | Prior to ES6, there was never any special reservation made on methods called `then(..)`, and as you can imagine there's been at least a few cases where that method name has been chosen prior to Promises ever showing up on the radar screen. The most likely case of mistaken thenable will be async libraries that use `then(..)` but which are not strictly Promises-compliant -- there are several out in the wild.
180 |
181 | The onus will be on you to guard against directly using values with the Promise mechanism that would be incorrectly assumed to be a thenable.
182 |
183 | ### `Promise` API
184 |
185 | The `Promise` API also provides some static methods for working with Promises.
186 |
187 | `Promise.resolve(..)` creates a promise resolved to the value passed in. Let's compare how it works to the more manual approach:
188 |
189 | ```js
190 | var p1 = Promise.resolve( 42 );
191 |
192 | var p2 = new Promise( function pr(resolve){
193 | resolve( 42 );
194 | } );
195 | ```
196 |
197 | `p1` and `p2` will have essentially identical behavior. The same goes for resolving with a promise:
198 |
199 | ```js
200 | var theP = ajax( .. );
201 |
202 | var p1 = Promise.resolve( theP );
203 |
204 | var p2 = new Promise( function pr(resolve){
205 | resolve( theP );
206 | } );
207 | ```
208 |
209 | **Tip:** `Promise.resolve(..)` is the solution to the thenable trust issue raised in the previous section. Any value that you are not already certain is a trustable promise -- even if it could be an immediate value -- can be normalized by passing it to `Promise.resolve(..)`. If the value is already a recognizable promise or thenable, its state/resolution will simply be adopted, insulating you from misbehavior. If it's instead an immediate value, it will be "wrapped" in a genuine promise, thereby normalizing its behavior to be async.
210 |
211 | `Promise.reject(..)` creates an immediately rejected promise, the same as its `Promise(..)` constructor counterpart:
212 |
213 | ```js
214 | var p1 = Promise.reject( "Oops" );
215 |
216 | var p2 = new Promise( function pr(resolve,reject){
217 | reject( "Oops" );
218 | } );
219 | ```
220 |
221 | While `resolve(..)` and `Promise.resolve(..)` can accept a promise and adopt its state/resolution, `reject(..)` and `Promise.reject(..)` do not differentiate what value they receive. So, if you reject with a promise or thenable, the promise/thenable itself will be set as the rejection reason, not its underlying value.
222 |
223 | `Promise.all([ .. ])` accepts an array of one or more values (e.g., immediate values, promises, thenables). It returns a promise back that will be fulfilled if all the values fulfill, or reject immediately once the first of any of them rejects.
224 |
225 | Starting with these values/promises:
226 |
227 | ```js
228 | var p1 = Promise.resolve( 42 );
229 | var p2 = new Promise( function pr(resolve){
230 | setTimeout( function(){
231 | resolve( 43 );
232 | }, 100 );
233 | } );
234 | var v3 = 44;
235 | var p4 = new Promise( function pr(resolve,reject){
236 | setTimeout( function(){
237 | reject( "Oops" );
238 | }, 10 );
239 | } );
240 | ```
241 |
242 | Let's consider how `Promise.all([ .. ])` works with combinations of those values:
243 |
244 | ```js
245 | Promise.all( [p1,p2,v3] )
246 | .then( function fulfilled(vals){
247 | console.log( vals ); // [42,43,44]
248 | } );
249 |
250 | Promise.all( [p1,p2,v3,p4] )
251 | .then(
252 | function fulfilled(vals){
253 | // never gets here
254 | },
255 | function rejected(reason){
256 | console.log( reason ); // Oops
257 | }
258 | );
259 | ```
260 |
261 | While `Promise.all([ .. ])` waits for all fulfillments (or the first rejection), `Promise.race([ .. ])` waits only for either the first fulfillment or rejection. Consider:
262 |
263 | ```js
264 | // NOTE: re-setup all test values to
265 | // avoid timing issues misleading you!
266 |
267 | Promise.race( [p2,p1,v3] )
268 | .then( function fulfilled(val){
269 | console.log( val ); // 42
270 | } );
271 |
272 | Promise.race( [p2,p4] )
273 | .then(
274 | function fulfilled(val){
275 | // never gets here
276 | },
277 | function rejected(reason){
278 | console.log( reason ); // Oops
279 | }
280 | );
281 | ```
282 |
283 | **Warning:** While `Promise.all([])` will fulfill right away (with no values), `Promise.race([])` will hang forever. This is a strange inconsistency, and speaks to the suggestion that you should never use these methods with empty arrays.
284 |
285 | ## Generators + Promises
286 |
287 | It *is* possible to express a series of promises in a chain to represent the async flow control of your program. Consider:
288 |
289 | ```js
290 | step1()
291 | .then(
292 | step2,
293 | step1Failed
294 | )
295 | .then(
296 | function step3(msg) {
297 | return Promise.all( [
298 | step3a( msg ),
299 | step3b( msg ),
300 | step3c( msg )
301 | ] )
302 | }
303 | )
304 | .then(step4);
305 | ```
306 |
307 | However, there's a much better option for expressing async flow control, and it will probably be much more preferable in terms of coding style than long promise chains. We can use what we learned in Chapter 3 about generators to express our async flow control.
308 |
309 | The important pattern to recognize: a generator can yield a promise, and that promise can then be wired to resume the generator with its fulfillment value.
310 |
311 | Consider the previous snippet's async flow control expressed with a generator:
312 |
313 | ```js
314 | function *main() {
315 |
316 | try {
317 | var ret = yield step1();
318 | }
319 | catch (err) {
320 | ret = yield step1Failed( err );
321 | }
322 |
323 | ret = yield step2( ret );
324 |
325 | // step 3
326 | ret = yield Promise.all( [
327 | step3a( ret ),
328 | step3b( ret ),
329 | step3c( ret )
330 | ] );
331 |
332 | yield step4( ret );
333 | }
334 | ```
335 |
336 | On the surface, this snippet may seem more verbose than the promise chain equivalent in the earlier snippet. However, it offers a much more attractive -- and more importantly, a more understandable and reason-able -- synchronous-looking coding style (with `=` assignment of "return" values, etc.) That's especially true in that `try..catch` error handling can be used across those hidden async boundaries.
337 |
338 | Why are we using Promises with the generator? It's certainly possible to do async generator coding without Promises.
339 |
340 | Promises are a trustable system that uninverts the inversion of control of normal callbacks or thunks (see the *Async & Performance* title of this series). So, combining the trustability of Promises and the synchronicity of code in generators effectively addresses all the major deficiencies of callbacks. Also, utilities like `Promise.all([ .. ])` are a nice, clean way to express concurrency at a generator's single `yield` step.
341 |
342 | So how does this magic work? We're going to need a *runner* that can run our generator, receive a `yield`ed promise, and wire it up to resume the generator with either the fulfillment success value, or throw an error into the generator with the rejection reason.
343 |
344 | Many async-capable utilities/libraries have such a "runner"; for example, `Q.spawn(..)` and my asynquence's `runner(..)` plug-in. But here's a stand-alone runner to illustrate how the process works:
345 |
346 | ```js
347 | function run(gen) {
348 | var args = [].slice.call( arguments, 1), it;
349 |
350 | it = gen.apply( this, args );
351 |
352 | return Promise.resolve()
353 | .then( function handleNext(value){
354 | var next = it.next( value );
355 |
356 | return (function handleResult(next){
357 | if (next.done) {
358 | return next.value;
359 | }
360 | else {
361 | return Promise.resolve( next.value )
362 | .then(
363 | handleNext,
364 | function handleErr(err) {
365 | return Promise.resolve(
366 | it.throw( err )
367 | )
368 | .then( handleResult );
369 | }
370 | );
371 | }
372 | })( next );
373 | } );
374 | }
375 | ```
376 |
377 | **Note:** For a more prolifically commented version of this utility, see the *Async & Performance* title of this series. Also, the run utilities provided with various async libraries are often more powerful/capable than what we've shown here. For example, asynquence's `runner(..)` can handle `yield`ed promises, sequences, thunks, and immediate (non-promise) values, giving you ultimate flexibility.
378 |
379 | So now running `*main()` as listed in the earlier snippet is as easy as:
380 |
381 | ```js
382 | run( main )
383 | .then(
384 | function fulfilled(){
385 | // `*main()` completed successfully
386 | },
387 | function rejected(reason){
388 | // Oops, something went wrong
389 | }
390 | );
391 | ```
392 |
393 | Essentially, anywhere that you have more than two asynchronous steps of flow control logic in your program, you can *and should* use a promise-yielding generator driven by a run utility to express the flow control in a synchronous fashion. This will make for much easier to understand and maintain code.
394 |
395 | This yield-a-promise-resume-the-generator pattern is going to be so common and so powerful, the next version of JavaScript after ES6 is almost certainly going to introduce a new function type that will do it automatically without needing the run utility. We'll cover `async function`s (as they're expected to be called) in Chapter 8.
396 |
397 | ## Review
398 |
399 | As JavaScript continues to mature and grow in its widespread adoption, asynchronous programming is more and more of a central concern. Callbacks are not fully sufficient for these tasks, and totally fall down the more sophisticated the need.
400 |
401 | Thankfully, ES6 adds Promises to address one of the major shortcomings of callbacks: lack of trust in predictable behavior. Promises represent the future completion value from a potentially async task, normalizing behavior across sync and async boundaries.
402 |
403 | But it's the combination of Promises with generators that fully realizes the benefits of rearranging our async flow control code to de-emphasize and abstract away that ugly callback soup (aka "hell").
404 |
405 | Right now, we can manage these interactions with the aide of various async libraries' runners, but JavaScript is eventually going to support this interaction pattern with dedicated syntax alone!
406 |
--------------------------------------------------------------------------------
/es-next-beyond/fig1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdras/You-Dont-Know-JS/ab5fdc081f75bfb6b1d677ec3e1a2c38776b4132/es-next-beyond/fig1.png
--------------------------------------------------------------------------------
/es-next-beyond/foreword.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: ES.Next & Beyond - 2nd Edition
2 | # Foreword
3 |
4 | | NOTE: |
5 | | :--- |
6 | | Work in progress |
7 |
--------------------------------------------------------------------------------
/es-next-beyond/toc.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: ES.Next & Beyond - 2nd Edition
2 |
3 | | NOTE: |
4 | | :--- |
5 | | Work in progress |
6 |
7 | ## Table of Contents
8 |
9 | * Foreword
10 | * Preface
11 | * Chapter 1: ES? Now & Future
12 | * Versioning
13 | * Transpiling
14 | * Chapter 2: Syntax
15 | * Block-Scoped Declarations
16 | * Spread / Rest
17 | * Default Parameter Values
18 | * Destructuring
19 | * Object Literal Extensions
20 | * Template Literals
21 | * Arrow Functions
22 | * `for..of` Loops
23 | * Regular Expression Extensions
24 | * Number Literal Extensions
25 | * Unicode
26 | * Symbols
27 | * Chapter 3: Organization
28 | * Iterators
29 | * Generators
30 | * Modules
31 | * Classes
32 | * Chapter 4: Async Flow Control
33 | * Promises
34 | * Generators + Promises
35 | * Chapter 5: Collections
36 | * TypedArrays
37 | * Maps
38 | * WeakMaps
39 | * Sets
40 | * WeakSets
41 | * Chapter 6: API Additions
42 | * `Array`
43 | * `Object`
44 | * `Math`
45 | * `Number`
46 | * `String`
47 | * Chapter 7: Meta Programming
48 | * Function Names
49 | * Meta Properties
50 | * Well Known Symbols
51 | * Proxies
52 | * `Reflect` API
53 | * Feature Testing
54 | * Tail Call Optimization (TCO)
55 | * Chapter 8: Beyond ES6
56 | * `async function`s
57 | * `Object.observe(..)`
58 | * Exponentiation Operator
59 | * Object Properties and `...`
60 | * `Array#includes(..)`
61 | * SIMD
62 | * Appendix A: TODO
63 |
--------------------------------------------------------------------------------
/get-started/README.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: Get Started - 2nd Edition
2 |
3 | [Table of Contents](toc.md)
4 |
5 | * [Foreword](foreword.md) (by [Brian Holt](https://twitter.com/holtbt))
6 | * [Preface](../preface.md)
7 | * [Chapter 1: What Is JavaScript?](ch1.md)
8 | * [Chapter 2: Surveying JS](ch2.md)
9 | * [Chapter 3: Digging To The Roots Of JS](ch3.md)
10 | * [Chapter 4: The Bigger Picture](ch4.md)
11 | * [Appendix A: Exploring Further](apA.md)
12 | * [Appendix B: Practice, Practice, Practice!](apB.md)
13 |
--------------------------------------------------------------------------------
/get-started/apA.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: Get Started - 2nd Edition
2 | # Appendix A: Exploring Further
3 |
4 | In this appendix, we're going to explore some topics from the main chapter text in a bit more detail. Think of this content as an optional preview of some of the more nuanced details covered throughout the rest of the book series.
5 |
6 | ## Values vs References
7 |
8 | In Chapter 2, we introduced the two main types of values: primitives and objects. But we didn't discuss yet one key difference between the two: how these values are assigned and passed around.
9 |
10 | In many languages, the developer can choose between assigning/passing a value as the value itself, or as a reference to the value. In JS, however, this decision is entirely determined by the kind of value. That surprises a lot of developers from other languages when they start using JS.
11 |
12 | If you assign/pass a value itself, the value is copied. For example:
13 |
14 | ```js
15 | var myName = "Kyle";
16 |
17 | var yourName = myName;
18 | ```
19 |
20 | Here, the `yourName` variable has a separate copy of the `"Kyle"` string from the value that's stored in `myName`. That's because the value is a primitive, and primitive values are always assigned/passed as **value copies**.
21 |
22 | Here's how you can prove there's two separate values involved:
23 |
24 | ```js
25 | var myName = "Kyle";
26 |
27 | var yourName = myName;
28 |
29 | myName = "Frank";
30 |
31 | console.log(myName);
32 | // Frank
33 |
34 | console.log(yourName);
35 | // Kyle
36 | ```
37 |
38 | See how `yourName` wasn't affected by the re-assignment of `myName` to `"Frank"`? That's because each variable holds its own copy of the value.
39 |
40 | By contrast, references are the idea that two or more variables are pointing at the same value, such that modifying this shared value would be reflected by an access via any of those references. In JS, only object values (arrays, objects, functions, etc) are treated as references.
41 |
42 | Consider:
43 |
44 | ```js
45 | var myAddress = {
46 | street: "123 JS Blvd",
47 | city: "Austin",
48 | state: "TX"
49 | };
50 |
51 | var yourAddress = myAddress;
52 |
53 | // I've got to move to a new house!
54 | myAddress.street = "456 TS Ave";
55 |
56 | console.log(yourAddress.street);
57 | // 456 TS Ave
58 | ```
59 |
60 | Because the value assigned to `myAddress` is an object, it's held/assigned by reference, and thus the assignment to the `yourAddress` variable is a copy of the reference, not the object value itself. That's why the updated value assigned to the `myAddress.street` is reflected the when we access `yourAddress.street`. `myAddress` and `yourAddress` have copies of the reference to the single shared object, so an update to one is an update to both.
61 |
62 | Again, JS chooses the value-copy vs reference-copy behavior based on the value type. Primitives are held by value, objects are held by reference. There's no way to override this in JS, in either direction.
63 |
64 | ## So Many Function Forms
65 |
66 | Recall this snippet from the "Functions" section in Chapter 2:
67 |
68 | ```js
69 | var awesomeFunction = function(coolThings) {
70 | // ..
71 | return amazingStuff;
72 | };
73 | ```
74 |
75 | The function expression above is referred to as an *anonymous function expression*, since it has no name identifier between the `function` keyword and the `(..)` parameter list. This point confuses many JS developers because as of ES6, JS performs a "name inference" on an anonymous function:
76 |
77 | ```js
78 | awesomeFunction.name;
79 | // "awesomeFunction"
80 | ```
81 |
82 | The `name` property of a function will reveal either its directly given name (in the case of a declaration) or its inferred name in the case of an anonymous function expression. That value is generally used by developer tools when inspecting a function value or when reporting an error stack trace.
83 |
84 | So even an anonymous function expression *might* get a name. However, name inference only happens in limited cases such as when the function expression is assigned (with `=`). If you pass a function expression as an argument to a function call, for example, no name inference occurs, the `name` property will be an empty string, and the developer console will usually report "(anonymous function)".
85 |
86 | Even if a name is inferred, **it's still an anonymous function.** Why? Because the inferred name is a metadata string value, not an available identifier to refer to the function. An anonymous function doesn't have an identifier to use to refer to itself from inside itself -- for recursion, event unbinding, etc.
87 |
88 | Compare the anonymous function expression form to:
89 |
90 | ```js
91 | // let awesomeFunction = ..
92 | // const awesomeFunction = ..
93 | var awesomeFunction = function someName(coolThings) {
94 | // ..
95 | return amazingStuff;
96 | };
97 |
98 | awesomeFunction.name;
99 | // "someName"
100 | ```
101 |
102 | This function expression is a *named function expression*, since the identifier `someName` is directly associated with the function expression at compile time; the association with the identifier `awesomeFunction` still doesn't happen until runtime at the time of that statement. Those two identifiers don't have to match; sometimes it makes sense to have them be different, othertimes it's better to have them be the same.
103 |
104 | Notice also that the explicit function name, the identifier `someName`, takes precedence when assigning a *name* for the `name` property.
105 |
106 | Should function expressions be named or anonymous? Opinions vary widely on this. Most developers tend to be unconcerned with using anonymous functions. They're shorter, and unquestionably more common in the broad sphere of JS code out there.
107 |
108 | In my opinion, if a function exists in your program, it has a purpose; otherwise, take it out! And if it has a purpose, it has a natural name that describes that purpose.
109 |
110 | If a function has a name, you the code author should include that name in the code, so that the reader does not have to infer that name from reading and mentally executing that function's source code. Even a trivial function body like `x * 2` has to be read to infer a name like "double" or "multBy2"; that brief extra mental work is unnecessary when you could just take a second to name the function "double" or "multBy2" *once*, saving the reader that repeated mental work every time it's read in the future.
111 |
112 | There are, regrettably in some respects, many other function definition forms in JS as of late 2019 (maybe more in the future!).
113 |
114 | Here are some more declaration forms:
115 |
116 | ```js
117 | // generator function declaration
118 | function *two() { .. }
119 |
120 | // async function declaration
121 | async function three() { .. }
122 |
123 | // async generator function declaration
124 | async function *four() { .. }
125 |
126 | // named function export declaration (ES6 modules)
127 | export function five() { .. }
128 | ```
129 |
130 | And here are some more of the (many!) function expression forms:
131 |
132 | ```js
133 | // IIFE
134 | (function(){ .. })();
135 | (function namedIIFE(){ .. })();
136 |
137 | // asynchronous IIFE
138 | (async function(){ .. })();
139 | (async function namedAIIFE(){ .. })();
140 |
141 | // arrow function expressions
142 | var f;
143 | f = () => 42;
144 | f = x => x * 2;
145 | f = (x) => x * 2;
146 | f = (x,y) => x * y;
147 | f = x => ({ x: x * 2 });
148 | f = x => { return x * 2; };
149 | f = async x => {
150 | var y = await doSomethingAsync(x);
151 | return y * 2;
152 | };
153 | someOperation( x => x * 2 );
154 | // ..
155 | ```
156 |
157 | Keep in mind that arrow function expressions are **syntactically anonymous**, meaning the syntax doesn't provide a way to provide a direct name identifier for the function. The function expression may get an inferred name, but only if it's one of the assignment forms, not in the (more common!) form of being passed as a function call argument (as in the last line of the snippet).
158 |
159 | Since I don't think anonymous functions are a good idea to use frequently in your programs, I'm not a fan of using the `=>` arrow function form. This kind of function actually has a specific purpose -- handling the `this` keyword lexically -- but that doesn't mean we should use it for every function we write. Use the most appropriate tool for each job.
160 |
161 | Functions can also be specified in class definitions and object literal definitions. They're typically referred to as "methods" when in these forms, though in JS this term doesn't have much observable difference over "function".
162 |
163 | ```js
164 | class SomethingKindaGreat {
165 | // class methods
166 | coolMethod() { .. } // no commas!
167 | boringMethod() { .. }
168 | }
169 |
170 | var EntirelyDifferent = {
171 | // object methods
172 | coolMethod() { .. }, // commas!
173 | boringMethod() { .. },
174 |
175 | // (anonymous) function expression property
176 | oldSchool: function() { .. }
177 | };
178 | ```
179 |
180 | Phew! That's a lot of different ways to define functions.
181 |
182 | There's no simple shortcut path here; you just have to build familiarity with all the function forms so you can recognize them in existing code and use them appropriately in the code you write. Study them closely and practice!
183 |
184 | ## Coercive Conditional Comparison
185 |
186 | Yes, that section name is quite a mouthful. But what are we talking about? We're talking about conditional expressions needing to perform coercion-oriented comparisons to make their decisions.
187 |
188 | `if` and `? :`-ternary statements, as well as the test clauses in `while` and `for` loops, all perform an implicit value comparison. But what sort? Is it "strict" or "coercive"? Both, actually.
189 |
190 | Consider:
191 |
192 | ```js
193 | var x = 1;
194 |
195 | if (x) {
196 | // will run!
197 | }
198 |
199 | while (x) {
200 | // will run, once!
201 | x = false;
202 | }
203 | ```
204 |
205 | You might think of these `(x)` conditional expressions like this:
206 |
207 | ```js
208 | var x = 1;
209 |
210 | if (x == true) {
211 | // will run!
212 | }
213 |
214 | while (x == true) {
215 | // will run, once!
216 | x = false;
217 | }
218 | ```
219 |
220 | In this specific case -- the value of `x` being `1` -- that mental model works, but it's not accurate more broadly. Consider:
221 |
222 | ```js
223 | var x = "hello";
224 |
225 | if (x) {
226 | // will run!
227 | }
228 |
229 | if (x == true) {
230 | // won't run :(
231 | }
232 | ```
233 |
234 | Oops. So what is the `if` statement actually doing? This is the more accurate mental model.
235 |
236 | ```js
237 | var x = "hello";
238 |
239 | if (Boolean(x) == true) {
240 | // will run
241 | }
242 |
243 | // which is the same as:
244 |
245 | if (Boolean(x) === true) {
246 | // will run
247 | }
248 | ```
249 |
250 | Since the `Boolean(..)` function always returns a value of type boolean, the `==` vs `===` in that above snippet is irrelevant; they'll both do the same thing. But the important part is to see that before the comparison, a coercion occurs, from whatever type `x` currently is, to boolean.
251 |
252 | You just can't get away from coercions in JS comparisons. Buckle down and learn them.
253 |
254 | ## Prototypal "Classes"
255 |
256 | In Chapter 3, we introduced prototypes and showed how we can link objects through a prototype chain.
257 |
258 | Another way of wiring up such prototype linkages served as the (honestly, ugly) predecessor to the elegance of the ES6 `class` system (see Chapter 2), and is referred to as prototypal classes.
259 |
260 | | TIP: |
261 | | :--- |
262 | | While this style of code is quite uncommon in JS these days, it's still perplexingly rather common to be asked about it in job interviews! |
263 |
264 | Let's first recall the `Object.create(..)` style of coding:
265 |
266 | ```js
267 | var Classroom = {
268 | welcome() {
269 | console.log("Welcome, students!");
270 | }
271 | };
272 |
273 | var mathClass = Object.create(Classroom);
274 |
275 | mathClass.welcome();
276 | // Welcome, students!
277 | ```
278 |
279 | Here, a `mathClass` object is linked via its prototype to a `Classroom` object. Through this link, the `mathClass.welcome()` function call is delegated to the method defined on `Classroom`.
280 |
281 | The prototypal class pattern would have labeled this delegation behavior "inheritance", and alternately have defined it (with the same behavior) as:
282 |
283 | ```js
284 | function Classroom() {
285 | // ..
286 | }
287 |
288 | Classroom.prototype.welcome = function hello() {
289 | console.log("Welcome, students!");
290 | };
291 |
292 | var mathClass = new Classroom();
293 |
294 | mathClass.welcome();
295 | // Welcome, students!
296 | ```
297 |
298 | All functions by default reference an empty object at a property named `prototype`. Despite the confusing naming, this is **not** the function's *prototype* -- where the function is prototype linked to -- but rather the prototype object to *link to* when other objects are created by calling the function with `new`.
299 |
300 | We add a `welcome` property to that empty `Classroom.prototype` object, pointing at a `hello()` function.
301 |
302 | Then `new Classroom()` creates a new object (assigned to `mathClass`), and prototype links it to the existing `Classroom.prototype` object.
303 |
304 | Though `mathClass` does not have a `welcome()` property/function, it successfully delegates to `Classroom.prototype.welcome()`.
305 |
306 | This "prototypal class" pattern is now strongly discouraged, in favor of using ES6's `class` mechanism:
307 |
308 | ```js
309 | class Classroom {
310 | constructor() {
311 | // ..
312 | }
313 |
314 | welcome() {
315 | console.log("Welcome, students!");
316 | }
317 | }
318 |
319 | var mathClass = new Classroom();
320 |
321 | mathClass.welcome();
322 | // Welcome, students!
323 | ```
324 |
325 | Under the covers, the same prototype linkage is wired up, but this `class` syntax fits the class-oriented design pattern much more cleanly than "prototypal classes".
326 |
--------------------------------------------------------------------------------
/get-started/apB.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: Get Started - 2nd Edition
2 | # Appendix B: Practice, Practice, Practice!
3 |
4 | In this appendix, we'll explore some exercises and their suggested solutions. These are just to *get you started* with practice over the concepts from the book.
5 |
6 | ## Practicing Comparisons
7 |
8 | Let's practice working with value types and comparisons (Chapter 4, Pillar 3) where coercion will need to be involved.
9 |
10 | `scheduleMeeting(..)` should take a start time (in 24hr format as a string "hh:mm") and a meeting duration (number of minutes). It should return `true` if the meeting falls entirely within the work day (according to the times specified in `dayStart` and `dayEnd`); return `false` if the meeting violates the work day bounds.
11 |
12 | ```js
13 | const dayStart = "07:30";
14 | const dayEnd = "17:45";
15 |
16 | function scheduleMeeting(startTime,durationMinutes) {
17 | // ..TODO..
18 | }
19 |
20 | scheduleMeeting("7:00",15); // false
21 | scheduleMeeting("07:15",30); // false
22 | scheduleMeeting("7:30",30); // true
23 | scheduleMeeting("11:30",60); // true
24 | scheduleMeeting("17:00",45); // true
25 | scheduleMeeting("17:30",30); // false
26 | scheduleMeeting("18:00",15); // false
27 | ```
28 |
29 | Try to solve this yourself first. Consider the usage of equality and relational comparison operators, and how coercion impacts this code. Once you have code that works, *compare* your solution(s) to the code in "Suggested Solutions" at the end of this appendix.
30 |
31 | ## Practicing Closure
32 |
33 | Now let's practice with closure (Chapter 4, Pillar 1).
34 |
35 | The `range(..)` function takes a number as its first argument, representing the first number in a desired range of numbers. The second argument is also a number representing the end of the desired range (inclusive). If the second argument is omitted, then another function should be returned that expects that argument.
36 |
37 | ```js
38 | function range(start,end) {
39 | // ..TODO..
40 | }
41 |
42 | range(3,3); // [3]
43 | range(3,8); // [3,4,5,6,7,8]
44 | range(3,0); // []
45 |
46 | var start3 = range(3);
47 | var start4 = range(4);
48 |
49 | start3(3); // [3]
50 | start3(8); // [3,4,5,6,7,8]
51 | start3(0); // []
52 |
53 | start4(6); // [4,5,6]
54 | ```
55 |
56 | Try to solve this yourself first.
57 |
58 | Once you have code that works, *compare* your solution(s) to the code in "Suggested Solutions" at the end of this appendix.
59 |
60 | ## Practicing Prototypes
61 |
62 | Finally, let's work on `this` and objects linked via prototype (Chapter 4, Pillar 2).
63 |
64 | Define a slot machine with 3 reels that can individually `spin()`, and then `display()` the current contents of all the reels.
65 |
66 | The basic behavior of a single reel is defined in the `reel` object below. But the slot machine needs individual reels -- objects that delegate to `reel`, and which each have a `position` property.
67 |
68 | A reel only *knows how* to `display()` its current slot symbol, but a slot machine typically shows 3 symbols per reel: the current slot (`position`), one slot above (`position - 1`), and one slot below (`position + 1`). So displaying the slot machine should end up displaying a 3 x 3 grid of slot symbols.
69 |
70 | ```js
71 | function randMax(max) {
72 | return Math.trunc(1E9 * Math.random()) % max;
73 | }
74 |
75 | var reel = {
76 | symbols: [ "♠", "♥", "♦", "♣", "☺", "★", "☾", "☀" ],
77 | spin() {
78 | if (this.position == null) {
79 | this.position = randMax(this.symbols.length - 1);
80 | }
81 | this.position = (
82 | this.position + 100 + randMax(100)
83 | ) % this.symbols.length;
84 | },
85 | display() {
86 | if (this.position == null) {
87 | this.position = randMax(this.symbols.length - 1);
88 | }
89 | return this.symbols[this.position];
90 | }
91 | };
92 |
93 | var slotMachine = {
94 | reels: [
95 | // this slot machine needs 3 separate reels
96 | // hint: Object.create(..)
97 | ],
98 | spin() {
99 | this.reels.forEach(function spinReel(reel){
100 | reel.spin();
101 | });
102 | },
103 | display() {
104 | // TODO
105 | }
106 | };
107 |
108 | slotMachine.spin();
109 | slotMachine.display();
110 | // ☾ | ☀ | ★
111 | // ☀ | ♠ | ☾
112 | // ♠ | ♥ | ☀
113 |
114 | slotMachine.spin();
115 | slotMachine.display();
116 | // ♦ | ♠ | ♣
117 | // ♣ | ♥ | ☺
118 | // ☺ | ♦ | ★
119 | ```
120 |
121 | Try to solve this yourself first.
122 |
123 | Hints:
124 |
125 | 1. use the `%` modulo operator for wrapping `position` as you access symbols circularly around a reel.
126 |
127 | 2. use `Object.create(..)` to create an object and prototype-link it to another object. Once linked, delegation allows the objects to share `this` context during method invocation.
128 |
129 | 3. instead of modifying the reel object directly to show each of the three positions, you can use another temporary object (`Object.create(..)` again) with its own `position`, to delegate from.
130 |
131 | Once you have code that works, *compare* your solution(s) to the code in "Suggested Solutions" at the end of this appendix.
132 |
133 | ## Suggested Solutions
134 |
135 | Keep in mind that these suggested solutions are just that: suggestions. There's many different ways to solve these practice exercises. Compare your approach to what you see here, and consider the pros and cons of each.
136 |
137 | Suggested solution for "Comparisons" (Pillar 3) practice:
138 |
139 | ```js
140 | const dayStart = "07:30";
141 | const dayEnd = "17:45";
142 |
143 | function scheduleMeeting(startTime,durationMinutes) {
144 | var [ , meetingStartHour, meetingStartMinutes ] =
145 | startTime.match(/^(\d{1,2}):(\d{2})$/) || [];
146 |
147 | durationMinutes = Number(durationMinutes);
148 |
149 | if (
150 | typeof meetingStartHour == "string" &&
151 | typeof meetingStartMinutes == "string"
152 | ) {
153 | let durationHours = Math.floor(durationMinutes / 60);
154 | durationMinutes = durationMinutes - (durationHours * 60);
155 | let meetingEndHour = Number(meetingStartHour) + durationHours;
156 | let meetingEndMinutes = Number(meetingStartMinutes) + durationMinutes;
157 |
158 | if (meetingEndMinutes > 60) {
159 | meetingEndHour = meetingEndHour + 1;
160 | meetingEndMinutes = meetingEndMinutes - 60;
161 | }
162 |
163 | // re-compose fully-qualified time strings
164 | // (to make comparison easier)
165 | let meetingStart = `${
166 | meetingStartHour.padStart(2,"0")
167 | }:${
168 | meetingStartMinutes.padStart(2,"0")
169 | }`;
170 | let meetingEnd = `${
171 | String(meetingEndHour).padStart(2,"0")
172 | }:${
173 | String(meetingEndMinutes).padStart(2,"0")
174 | }`;
175 |
176 | // NOTE: since expressions are all strings,
177 | // comparisons here are alphabetic, but that's
178 | // safe here since they're fully qualified
179 | // time strings (ie, "07:15" < "07:30")
180 | return (
181 | meetingStart >= dayStart &&
182 | meetingEnd <= dayEnd
183 | );
184 | }
185 |
186 | return false;
187 | }
188 |
189 | scheduleMeeting("7:00",15); // false
190 | scheduleMeeting("07:15",30); // false
191 | scheduleMeeting("7:30",30); // true
192 | scheduleMeeting("11:30",60); // true
193 | scheduleMeeting("17:00",45); // true
194 | scheduleMeeting("17:30",30); // false
195 | scheduleMeeting("18:00",15); // false
196 | ```
197 |
198 | Suggested solution for "Closure" (Pillar 1) practice:
199 |
200 | ```js
201 | function range(start,end) {
202 | start = Number(start) || 0;
203 |
204 | if (end === undefined) {
205 | return function getEnd(end) {
206 | return getRange(start,end);
207 | };
208 | }
209 | else {
210 | end = Number(end) || 0;
211 | return getRange(start,end);
212 | }
213 |
214 |
215 | // **********************
216 |
217 | function getRange(start,end) {
218 | var ret = [];
219 | for (let i = start; i <= end; i++) {
220 | ret.push(i);
221 | }
222 | return ret;
223 | }
224 | }
225 |
226 | range(3,3); // [3]
227 | range(3,8); // [3,4,5,6,7,8]
228 | range(3,0); // []
229 |
230 | var start3 = range(3);
231 | var start4 = range(4);
232 |
233 | start3(3); // [3]
234 | start3(8); // [3,4,5,6,7,8]
235 | start3(0); // []
236 |
237 | start4(6); // [4,5,6]
238 | ```
239 |
240 | Suggested solution for "Prototypes" (Pillar 2) practice:
241 |
242 | ```js
243 | function randMax(max) {
244 | return Math.trunc(1E9 * Math.random()) % max;
245 | }
246 |
247 | var reel = {
248 | symbols: [ "♠", "♥", "♦", "♣", "☺", "★", "☾", "☀" ],
249 | spin() {
250 | if (this.position == null) {
251 | this.position = randMax(this.symbols.length - 1);
252 | }
253 | this.position = (
254 | this.position + 100 + randMax(100)
255 | ) % this.symbols.length;
256 | },
257 | display() {
258 | if (this.position == null) {
259 | this.position = randMax(this.symbols.length - 1);
260 | }
261 | return this.symbols[this.position];
262 | }
263 | };
264 |
265 | var slotMachine = {
266 | reels: [
267 | Object.create(reel),
268 | Object.create(reel),
269 | Object.create(reel)
270 | ],
271 | spin() {
272 | this.reels.forEach(function spinReel(reel){
273 | reel.spin();
274 | });
275 | },
276 | display() {
277 | var lines = [];
278 |
279 | // display all 3 lines on the slot machine
280 | for (let linePos = -1; linePos <= 1; linePos++) {
281 | let line = this.reels.map(function getSlot(reel){
282 | var slot = Object.create(reel);
283 | slot.position = (
284 | reel.symbols.length + reel.position + linePos
285 | ) % reel.symbols.length;
286 | return reel.display.call(slot);
287 | });
288 | lines.push(line.join(" | "));
289 | }
290 |
291 | return lines.join("\n");
292 | }
293 | };
294 |
295 | slotMachine.spin();
296 | slotMachine.display();
297 | // ☾ | ☀ | ★
298 | // ☀ | ♠ | ☾
299 | // ♠ | ♥ | ☀
300 |
301 | slotMachine.spin();
302 | slotMachine.display();
303 | // ♦ | ♠ | ♣
304 | // ♣ | ♥ | ☺
305 | // ☺ | ♦ | ★
306 | ```
307 |
308 | That's it for this book. But now it's time to look for real projects to practice these ideas on. Just keep coding, because that's the best way to learn!
309 |
--------------------------------------------------------------------------------
/get-started/ch4.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: Get Started - 2nd Edition
2 | # Chapter 4: The Bigger Picture
3 |
4 | This book surveys what you need to be aware of as you *get started* with JS. The goal is to fill in gaps that readers newer to JS might have tripped over in their early encounters with the language. I also hope that we've hinted at enough deeper detail throughout to pique your curiosity to want to dig more into the language.
5 |
6 | The rest of the books in this series are where we will unpack all of the rest of the language, in far greater detail than we could have done in a few brief chapters here.
7 |
8 | Remember to take your time, though. Rather than rushing onto the next book in an attempt to churn through all the books expediently, spend some time going back over the material in this book. Spend some more time looking through code in your current projects, and comparing what you see to what's been discussed so far.
9 |
10 | When you're ready, this final chapter divides the organization of the JS language into three main pillars, then offers a brief roadmap of what to expect from the rest of the book series, and how I suggest you proceed. Also, don't skip the appendices, especially Appendix B, "Practice, Practice, Practice!".
11 |
12 | ## Pillar 1: Scope and Closure
13 |
14 | The organization of variables into units of scope (functions, blocks) is one of the most foundational characteristics of any language; perhaps no other characteristic has a greater impact on how programs behave.
15 |
16 | Scopes are like buckets, and variables are like marbles you put into those buckets. The scope model of a language is like the rules that help you determine which color marbles go in which matching-color buckets.
17 |
18 | Scopes nest inside each other, and for any given expression or statement, only variables at that level of scope nesting, or in higher/outer scopes, are accessible; variables from lower/inner scopes are hidden and inaccessible.
19 |
20 | This is how scopes behave in most languages, which is called lexical scope. The scope unit boundaries, and how variables are organized in them, is determined at the time the program is parsed (compiled). In other words, it's an author-time decision: where you locate a function/scope in the program determines what the scope structure of that part of the program will be.
21 |
22 | JS is lexically scoped, though many claim it isn't, because of two particular characteristics of its model that are not present in other lexically scoped languages.
23 |
24 | The first is commonly called *hoisting*: when all variables declared anywhere in a scope are treated as if they're declared at the beginning of the scope. The other is that `var` declared variables are function scoped, even if they appear inside a block.
25 |
26 | Neither hoisting nor function-scoped `var` are sufficient to back the claim that JS is not lexically scoped. `let` / `const` declarations have a peculiar error behavior called the "Temporal Dead Zone" (TDZ) which results in observable but unusable variables. Though TDZ can be strange to encounter, it's *also* not an invalidation of lexical scoping. All of these are just unique parts of the language that should be learned and understood by all JS developers.
27 |
28 | Closure is a natural result of lexical scope when the language has functions as first-class values, as JS does. When a function makes reference to variables from an outer scope, and that function is passed around as a value and executed in other scopes, it maintains access to its original scope variables; this is closure.
29 |
30 | Across all of programming, but especially in JS, closure drives many of the most important programming patterns, including modules. As I see it, modules are as *with the grain* as you can get, when it comes to code organization in JS.
31 |
32 | To dig further into scope, closures, and how modules work, read Book 2, *Scope & Closures*.
33 |
34 | ## Pillar 2: Prototypes
35 |
36 | The second pillar of the language is the prototypes system. We covered this topic in-depth in Chapter 3 ("Prototypes"), but I just want to make a few more comments about its importance.
37 |
38 | JS is one of very few languages where you have the option to create objects directly and explicitly, without first defining their structure in a class.
39 |
40 | For many years, people implemented the class design pattern on top of prototypes -- so called, "prototypal inheritance" (see Appendix A) -- and then with the advent of ES6's `class` keyword, the language doubled-down on its inclination towards OO/class style programming.
41 |
42 | But I think that focus has obscured the beauty and power of the prototype system: the ability for two objects to simply connect with each other and cooperate dynamically (during function/method execution) through sharing a `this` context.
43 |
44 | Classes are just one pattern you can build on top of such power. But another approach, in a very different direction, is to simply embrace objects as objects, forget classes altogether, and let objects cooperate through the prototype chain. This is called *behavior delegation*. I think delegation is more powerful than class inheritance, as a means for organizing behavior and data in our programs.
45 |
46 | But class inheritance gets almost all the attention. And the rest goes to functional programming (FP), as the sort of "anti-class" way of designing programs. This saddens me, because it snuffs out any chance for exploration of delegation as a viable alternative.
47 |
48 | I encourage you to spend plenty of time deep in Book 3, *Objects & Classes*, to see how object delegation holds far more potential than we've perhaps realized. This isn't an anti-`class` message, but it is intentionally a "classes aren't the only way to use objects" message that I want more JS developers to consider.
49 |
50 | Object delegation is, I would argue, far more *with the grain* of JS, than classes (more on *grains* in a bit).
51 |
52 | ## Pillar 3: Types and Coercion
53 |
54 | The third pillar of JS is by far the most overlooked part of JS's nature.
55 |
56 | The vast majority of developers have strong misconceptions about how *types* work in programming languages, and especially how they work in JS. A tidal wave of interest in the broader JS community has begun to shift to "static typing" approaches, using type-aware tooling like TypeScript or Flow.
57 |
58 | I agree that JS developers should learn more about types, and should learn more about how JS manages type conversions. I also agree that type-aware tooling can help developers, assuming they have gained and used this knowledge in the first place!
59 |
60 | But I don't agree at all that the inevitable conclusion of this is to decide JS's type mechanism is bad and that we need to cover up JS's types with solutions outside the language. We don't have to follow the "static typing" way to be smart and solid with types in our programs. There are other options, if you're just willing to go *against the grain* of the crowd, and *with the grain* of JS (again, more on that below).
61 |
62 | Arguably, this pillar is more important than the other two, in the sense that no JS program will do anything useful if it doesn't properly leverage JS's value types, as well as the conversion (coercion) of values between types.
63 |
64 | Even if you love TypeScript / Flow, you are not going to get the most out of those tools or coding approaches if you aren't deeply familiar with how the language itself manages value types.
65 |
66 | To learn more about JS types and coercion, check out Book 4, *Types & Grammar*. But please don't skip over this topic just because you've always heard that we should use `===` and forget about the rest.
67 |
68 | Without learning this pillar, your foundation in JS is shaky and incomplete at best.
69 |
70 | ## With The Grain
71 |
72 | I have some advice to share on continuing your learning journey with JS, and your path through the rest of this book series: be aware of the *grain* -- recall various references to *grain* earlier in this chapter.
73 |
74 | First, consider the *grain* (as in, wood) of how most people approach and use JS. You've probably already noticed that these books cut against that *grain* in many respects. In YDKJSY, I respect you the reader enough to explain all the parts of JS, not only some select popular parts. I believe you're both capable and deserving of that knowledge.
75 |
76 | But that is not what you'll find from a lot of other material out there. It also means that the more you follow and adhere to the guidance from these books -- that you think carefully and analyze for yourself what's best in your code -- the more you will stand out. That can be a good and bad thing. If you ever want to break out from the crowd, you're going to have to break from how the crowd does it!
77 |
78 | But I've also had many people tell me that they quoted some topic/explanation from these books during a job interview, and the interviewer told the candidate they were wrong; indeed, people have reportedly lost out on job offers as a result.
79 |
80 | As much as possible, I endeavor in these books to provide completely accurate information about JS, informed generally from the specification itself. But I also dose out quite a bit of my opinions on how you can interpret and use JS to the best benefit in your programs. I don't present opinion as fact, or vice versa. You'll always know which is which in these books.
81 |
82 | Facts about JS are not really up for debate. Either the specification says something, or it doesn't. If you don't like what the specification says, or my relaying of it, take that up with TC39! If you're in an interview and they claim you're wrong on the facts, ask them right then and there if you can look it up in the specification. If the interviewer won't re-consider, then you shouldn't want to work there anyway.
83 |
84 | But if you choose to align with my opinions, you have to be prepared to back up those choices with *why* you feel that way. Don't just parrot what I say. Own your opinions. Defend them. And if someone you were hoping to work with disagrees, walk away with your head still held high. It's a big JS, and there's plenty of room for lots of different ways.
85 |
86 | In other words, don't be afraid to go against the *grain*, as I have done with these books and all my teachings. Nobody can tell you how you will best make use of JS; that's for you to decide. I'm merely trying to empower you in coming to your own conclusions, no matter what they are.
87 |
88 | On the other hand, there's a *grain* you really should pay attention to and follow: the *grain* of how JS works, at the language level. There are things that work well and naturally in JS, given the right practice and approach, and there are things you really shouldn't try to do in the language.
89 |
90 | Can you make your JS program look like a Java, C#, or Perl program? What about Python or Ruby, or even PHP? To varying degrees, sure you can. But should you?
91 |
92 | No, I don't think you should. I think you should learn and embrace the JS way, and make your JS programs as JS'y as is practical. Some will think that means sloppy and informal programming, but I don't mean that at all. I just mean that JS has a lot of patterns and idioms that are recognizably "JS", and going with that *grain* is the general path to best success.
93 |
94 | Finally, maybe the most important *grain* to recognize is how the existing program(s) you're working on, and developers you're working with, do stuff. Don't read these books and then try to change *all that grain* in your existing projects over night. That approach will always fail.
95 |
96 | You'll have to shift these things little by little, over time. Work on building consensus with your fellow developers on why it's important to re-visit and re-consider an approach. But do so with just one small topic at a time, and let before-and-after code comparisons do most of the talking. Bring everyone on the team together to discuss, and push for decisions that are based on analysis and evidence from the code rather than the inertia of, "our senior devs have always done it this way".
97 |
98 | That's the most important advice I can impart to help you learn JS. Always keep looking for better ways to use what JS gives us to author more readable code. Everyone who works on your code, including your future self, will thank you!
99 |
100 | ## In Order
101 |
102 | So now you've got a broader perspective on what's left to explore in JS, and the right attitude to approach the rest of your journey.
103 |
104 | But one of the most common practical questions I get at this point is, "What order should I read the books?" There is a straightforward answer... but it also depends.
105 |
106 | My suggestion for most readers is to proceed through this series like in this order:
107 |
108 | 1. Get started with a solid foundation of JS from *Get Started* (Book 1) -- good news, you've already almost finished this book!
109 |
110 | 2. In *Scope & Closures* (Book 2), dig into the first pillar of JS: lexical scope, how that supports closure, and how the module pattern organizes code.
111 |
112 | 3. In *Objects & Classes* (Book 3), focus on the second pillar of JS: how JS's `this` works, how object prototypes support delegation, and how prototypes enable the `class` mechanism for OO-style code organization.
113 |
114 | 4. In *Types & Grammar* (Book 4), tackle the third and final pillar of JS: types and type coercion, as well as how JS's syntax and grammar define how we write our code.
115 |
116 | 5. With the **three pillars** solidly in place, *Sync & Async* (Book 5) then explores how we use flow control to model state change in our programs, both synchronously (right away) and asynchronously (over time).
117 |
118 | 6. The series concludes with *ES.Next & Beyond* (Book 6), a forward look at the near- and mid-term future of JS, including a variety of features likely coming to your JS programs before too long.
119 |
120 | That's the intended order to read this book series.
121 |
122 | However, books 2, 3, and 4 can generally be read in any order, depending on which topic you feel most curious about and comfortable exploring first. But I don't recommend you skip any of these three books -- not even *Types & Grammar*, as some of you will be tempted to do! -- even if you think you already have that topic down.
123 |
124 | Book 5 (*Sync & Async*) is crucial for deeply understanding JS, but if you start digging in and find it's too intimidating, this book can be deferred until you're more experienced with the language. The more JS you've written (and struggled with!), the more you'll come to appreciate this book. So don't be afraid to come back to it at a later time.
125 |
126 | The final book in the series, *ES.Next & Beyond*, in some respects stands alone. It can be read at the end, as I suggest, or right after *Getting Started* if you're looking for a shortcut to broaden your radar of what JS is all about. This book will also be more likely to receive updates in the future, so you'll probably want to re-visit it occasionally.
127 |
128 | However you choose to proceed with YDKJSY, check out the appendices of this book first, especially practicing the snippets in Appendix B, "Practice, Practice, Practice!" Did I mention you should go practice!? There's no better way to learn code than to write it.
129 |
--------------------------------------------------------------------------------
/get-started/fig1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdras/You-Dont-Know-JS/ab5fdc081f75bfb6b1d677ec3e1a2c38776b4132/get-started/fig1.png
--------------------------------------------------------------------------------
/get-started/fig2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdras/You-Dont-Know-JS/ab5fdc081f75bfb6b1d677ec3e1a2c38776b4132/get-started/fig2.png
--------------------------------------------------------------------------------
/get-started/fig3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdras/You-Dont-Know-JS/ab5fdc081f75bfb6b1d677ec3e1a2c38776b4132/get-started/fig3.png
--------------------------------------------------------------------------------
/get-started/fig4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdras/You-Dont-Know-JS/ab5fdc081f75bfb6b1d677ec3e1a2c38776b4132/get-started/fig4.png
--------------------------------------------------------------------------------
/get-started/fig5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdras/You-Dont-Know-JS/ab5fdc081f75bfb6b1d677ec3e1a2c38776b4132/get-started/fig5.png
--------------------------------------------------------------------------------
/get-started/fig6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdras/You-Dont-Know-JS/ab5fdc081f75bfb6b1d677ec3e1a2c38776b4132/get-started/fig6.png
--------------------------------------------------------------------------------
/get-started/foreword.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: Get Started - 2nd Edition
2 | # Foreword
3 |
4 | The first time I saw a tweet advertising the crowdfunding campaign for the original book series *You Don't Know JS*, I thought to myself that whoever the hell this Kyle Simpson is can get bent. Of course I know JavaScript! At the time I had been working in JavaScript for some years with some of tech's biggest names so I felt justified in looking down my nose at this sweeping claim.
5 |
6 | Once the funding for the book completed, I observed that a lot of people were really excited to read this book series and I decided to go ahead and give it a shot, you know, just to show everyone that I *do know* JavaScript. Once I dug in and poured over the texts, I experienced a profound mix of learning, surprise, and even a little anger. Kyle has a knack for saying something that challenges my world-view and making me think about it until I realize what he said was actually true (I'll never admit this to him though!).
7 |
8 | So it turned out I didn't know JavaScript. I didn't know why I had adopted certain patterns; I didn't know why JavaScript behaves the way it does in certain situations, and I didn't know much of the nuance of the languages that I assumed I did. Many were things I didn't know I didn't know, and I was a worse-off as a developer for it.
9 |
10 | This is what this book series is great for. It's not just for someone picking up the language for the first time (though it's for them, too); it's for all software craftspeople who want to master their tools, who want to understand the ins-and-outs of their trade, and who want to select the proper methods for solving problems.
11 |
12 | The thing I appreciate about Kyle and his work is that he is delightfully uninfluenced by the zeitgeist of the programming world around him. That isn't to say he isn't aware of what's happening in the community but that it doesn't sway his pursuit of the best answers to the correct questions. This often has put him at odds with what's the latest "best practices", but in reality this is exactly what you need: a perspective removed from temporal influence and simply speaking to the underlying truth. That's why this series is so good. The first edition of *You Don't Know JS* is still accurate, years later! Not many things stand this test of time given the shifting sands of the JavaScript landscape.
13 |
14 | Let's talk a moment about this first book itself, *Get Started*. You may be tempted to skip it as you may think that you already have "gotten started", but it's worth reading this one anyway! You'd be surprised how much depth, nuance, and oddity there is in the underlying building blocks of JavaScript, and it's quite essential for you to grapple with these underpinnings before venturing forward into the constructs of the language. It's exactly the sort of foundation you need, to really know JavaScript.
15 |
16 | So, do your future self a favor and dig into this book and unlock the knowledge within. These solid foundations will serve you better than any framework ever will; those come and go but we'll still be writing JavaScript itself for decades to come. Keep an open mind and challenge your preconceived notions.
17 |
18 | Because, as I found out myself, you probably don't know JavaScript (yet).
19 |
20 | Brian Holt
21 | Senior Program Manager on Visual Studio Code and Node.js on Azure
22 | Microsoft
23 |
--------------------------------------------------------------------------------
/get-started/toc.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: Get Started - 2nd Edition
2 |
3 | ## Table of Contents
4 |
5 | * Foreword
6 | * Preface
7 | * Chapter 1: What Is JavaScript?
8 | * About This Book
9 | * What's With That Name?
10 | * Language Specification
11 | * Many Faces
12 | * Backwards & Forwards
13 | * What's In an Interpretation?
14 | * Strictly Speaking
15 | * Defined
16 | * Chapter 2: Surveying JS
17 | * Files As Programs
18 | * Values
19 | * Declaring And Using Variables
20 | * Functions
21 | * Comparisons
22 | * How We Organize In JS
23 | * The Rabbit Hole Deepens
24 | * Chapter 3: Digging To The Roots Of JS
25 | * Closure
26 | * `this` Keyword
27 | * Prototypes
28 | * Iteration
29 | * Asking Why
30 | * Chapter 4: The Bigger Picture
31 | * Pillar 1: Scope and Closure
32 | * Pillar 2: Prototypes
33 | * Pillar 3: Types and Coercion
34 | * With The Grain
35 | * In Order
36 | * Appendix A: Exploring Further
37 | * Values vs References
38 | * So Many Function Forms
39 | * Coercive Conditional Comparison
40 | * Prototypal "Classes"
41 | * Appendix B: Practice, Practice, Practice!
42 | * Practicing Comparisons
43 | * Practicing Closure
44 | * Practicing Prototypes
45 | * Suggested Solutions
46 |
--------------------------------------------------------------------------------
/getting-started/README.md:
--------------------------------------------------------------------------------
1 | Moved to: https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/get-started/README.md
2 |
--------------------------------------------------------------------------------
/objects-classes/README.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: Objects & Classes - 2nd Edition
2 |
3 | | NOTE: |
4 | | :--- |
5 | | Work in progress |
6 |
7 | [Table of Contents](toc.md)
8 |
9 | * [Foreword](foreword.md) (by TBA)
10 | * [Preface](../preface.md)
11 | * [Chapter 1: `this` Or That?](ch1.md)
12 | * [Chapter 2: `this` All Makes Sense Now!](ch2.md)
13 | * [Chapter 3: Objects](ch3.md)
14 | * [Chapter 4: Mixing (Up) "Class" Objects](ch4.md)
15 | * [Chapter 5: Prototypes](ch5.md)
16 | * [Chapter 6: Behavior Delegation](ch6.md)
17 | * [Appendix A: ES6 `class`](apA.md)
18 |
--------------------------------------------------------------------------------
/objects-classes/apA.md:
--------------------------------------------------------------------------------
1 | # You Don't Know JS Yet: Objects & Classes - 2nd Edition
2 | # Appendix A: ES6 `class`
3 |
4 | | NOTE: |
5 | | :--- |
6 | | Work in progress |
7 |
8 | .
9 |
10 | .
11 |
12 | .
13 |
14 | .
15 |
16 | .
17 |
18 | .
19 |
20 | .
21 |
22 | ----
23 |
24 | | NOTE: |
25 | | :--- |
26 | | Everything below here is previous text from 1st edition, and is only here for reference while 2nd edition work is underway. **Please ignore this stuff.** |
27 |
28 | If there's any take-away message from the second half of this book (Chapters 4-6), it's that classes are an optional design pattern for code (not a necessary given), and that furthermore they are often quite awkward to implement in a `[[Prototype]]` language like JavaScript.
29 |
30 | This awkwardness is *not* just about syntax, although that's a big part of it. Chapters 4 and 5 examined quite a bit of syntactic ugliness, from verbosity of `.prototype` references cluttering the code, to *explicit pseudo-polymorphism* (see Chapter 4) when you give methods the same name at different levels of the chain and try to implement a polymorphic reference from a lower-level method to a higher-level method. `.constructor` being wrongly interpreted as "was constructed by" and yet being unreliable for that definition is yet another syntactic ugly.
31 |
32 | But the problems with class design are much deeper. Chapter 4 points out that classes in traditional class-oriented languages actually produce a *copy* action from parent to child to instance, whereas in `[[Prototype]]`, the action is **not** a copy, but rather the opposite -- a delegation link.
33 |
34 | When compared to the simplicity of OLOO-style code and behavior delegation (see Chapter 6), which embrace `[[Prototype]]` rather than hide from it, classes stand out as a sore thumb in JS.
35 |
36 | ## `class`
37 |
38 | But we *don't* need to re-argue that case again. I re-mention those issues briefly only so that you keep them fresh in your mind now that we turn our attention to the ES6 `class` mechanism. We'll demonstrate here how it works, and look at whether or not `class` does anything substantial to address any of those "class" concerns.
39 |
40 | Let's revisit the `Widget` / `Button` example from Chapter 6:
41 |
42 | ```js
43 | class Widget {
44 | constructor(width,height) {
45 | this.width = width || 50;
46 | this.height = height || 50;
47 | this.$elem = null;
48 | }
49 | render($where){
50 | if (this.$elem) {
51 | this.$elem.css( {
52 | width: this.width + "px",
53 | height: this.height + "px"
54 | } ).appendTo( $where );
55 | }
56 | }
57 | }
58 |
59 | class Button extends Widget {
60 | constructor(width,height,label) {
61 | super( width, height );
62 | this.label = label || "Default";
63 | this.$elem = $( "