JavaScript: The New Toys - Corrections and Clarifications
Inevitably, despite my best efforts of those of the the tech editors, the copy editors, the typesetters, and everyone else involved in making a book like this, sometimes mistakes are bound to creep in. I missed them on my final read-through so they all ultimately lay at my feet. Sorry! Here's a list of corrections/clarifications and a very short description; click the link for details:
- Chapter 4, Page 90
In a code block, "new Color
" should be "new ctor
" - Chapter 5, Page 113
A sentence has "Example.prototype
" when it should be "Example2.prototype
" - Chapter 8, Page 211
In a call in a code block, it usese "showUpdatedScore
" when it should be "showUpdates
" - Chapter 8, Page 218
A sentence has "handleErrorFromOriginalPromise
" when it should be "handleErrorFromThenHandler
" - Chapter 9, Page 230
I forgot an important caveat aboutreturn await
- Chapter 14, Page 369
A paragraph referred tod1
in three places when it should have just beend
- Chapter 14, Page 393
A comment saying0
would be shown should sayundefined
would be shown - Chapter 17, Page 473
A pair of curly quotes (“”) rather than straight quotes ("") snuck into a code block 🙂
Chapter 4, Page 90:
...about half-way down:
Again, though, it may be a bit odd for a static method to do that (depending on your use case). Let’s look at doing it in the prototype method
halfBright
:halfBright() { const ctor = this && this.constructor && this.constructor[Symbol.species] || Color; return new Color( Math.round(this.r / 2), Math.round(this.g / 2), Math.round(this.b / 2) ); }
In that code, "new Color
" should be "new ctor
". The corrected version is:
halfBright() { const ctor = this && this.constructor && this.constructor[Symbol.species] || Color; return new ctor( Math.round(this.r / 2), Math.round(this.g / 2), Math.round(this.b / 2) ); }
Chapter 5, Page 113:
...at the end of the second to last paragraph:
...But the call to
new Example2().toString()
returned"[object Example2]"
because the property was there (inherited fromExample.prototype
) and had the value"Example2"
.
"Example.prototype
" should be "Example2.prototype
". The corrected version is:
...But the call to
new Example2().toString()
returned"[object Example2]"
because the property was there (inherited fromExample2.prototype
) and had the value"Example2"
.
Chapter 8, Page 211:
In the code block under the third paragraph, it uses the wrong function name:
Every layer needs to either handle rejections or return a promise to its caller, expecting the caller to handle rejections. The top level has to do the rejection handling, since it has nothing it can pass the chain onto:
button.addEventListener("click", () => { const {scoreId, nameId} = this.dataset; showUpdatedScore(scoreId, nameId).catch(reportError); }
That should be "showUpdates
", not "showUpdatedScore
". The corrected version is:
button.addEventListener("click", () => { const {scoreId, nameId} = this.dataset; showUpdates(scoreId, nameId).catch(reportError); }
Chapter 8, Page 218:
Right at the top of the page, it refers to the code block at the end of page 217 and says:
That assumes
handleErrorFromOriginalPromise
never throws and doesn’t return a promise that gets rejected).
It should be "handleErrorFromThenHandler
", not "handleErrorFromOriginalPromise
". The corrected version is:
That assumes
handleErrorFromThenHandler
never throws and doesn’t return a promise that gets rejected).
Chapter 9, Page 230:
In the section "You Don't Need return await
," there was an important caveat I intended to include in the text but forgot to. You don't need return await
outside a try
block. But inside a try
block, return await
matters and can be useful.
The section referred to this fetchJSON
function:
async function fetchJSON(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
}
That code doesn't need return await
because the return
isn't in a try
block. But suppose you have an Error
subclass that you want to wrap all fetch
errors in. You'd need the await
if you were using a try
/catch
like this:
async function fetchJSON(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return await response.json();
// ^^^^^−−−−− note
} catch (e) {
throw new FetchError(e);
}
}
Without it, a rejection of the promise from response.json()
wouldn't be caught by the try
/catch
, and the caller would see the rejection reason from response.json()
directly, not with a FetchError
wrapper on it. Similarly, it matters even without catch
. For instance, suppose you maintained some counter of active fetchJSON
calls and you didn't want to decrement the counter until the full response was read (including the response body). You might use try
/finally
:
let activeFetches = 0;
async function fetchJSON(url) {
++activeFetches;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return await response.json();
// ^^^^^−−−−− note
} finally {
--activeFetches;
}
}
The await
changes when the finally
block's code runs: With the await
, the finally
code doesn't run until the promise from response.json()
settles, but without the await
, it runs when the return response.json()
returns the promise.
You don't need it outside of a try
block, as in the code the chapter was referring to, but you might — probably do — want it if the return
is in a try
.
Chapter 14, Page 369:
In the paragraph after the first code block, it repeatedly refers to the identifier "d1
", but the identifier in the code is just "d
". The corrected version is:
DoubleProduct
’s result accessor needs to usesuper.result
to run the result accessor fromProduct
, notDoubleProduct
, because if it used the version fromDoubleProduct
, it would call itself recursively and cause a stack overflow. But when calling theProduct
version of the result accessor, it has to make sure this is set to the instance (d
) so that the correct values forx
andy
get used. The object it gets the accessor from (Product.prototype
, the prototype of the prototype ofd
) and the object that should be this within the call to the accessor (d
) are different objects.
Chapter 14, Page 393:
About a quarter of the way down the page it has:
That seems like it should do the trick, right? Let’s do the same series of operations on it that we did before, but using const p = getCounter("p") instead of const c = new Counter("c") :
const p = getCounter("p"); console.log("p.value before increment:"); console.log(p.value); // 0 console.log("p._value before increment:"); console.log(p._value); // undefined p.increment(); // Throws an error!
That "0
" in the comment on the third line should be "undefined
". The corrected version is:
const p = getCounter("p"); console.log("p.value before increment:"); console.log(p.value); // undefined console.log("p._value before increment:"); console.log(p._value); // undefined p.increment(); // Throws an error!
Chapter 17, Page 473:
In the "Number.isFinite
, Number.isNaN
" section, about midway down the page, the first line of code has curly quotes:
const s = “42”;
...when it should nave straight ones:
const s = "42";
Have a question or comment about this post? Ping me on Mastodon at @tjcrowdertech@hachyderm.io!