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:

...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 from Example.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 from Example2.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 use super.result to run the result accessor from Product, not DoubleProduct, because if it used the version from DoubleProduct, it would call itself recursively and cause a stack overflow. But when calling the Product version of the result accessor, it has to make sure this is set to the instance (d) so that the correct values for x and y get used. The object it gets the accessor from (Product.prototype, the prototype of the prototype of d) 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!