(This page has no text content)
Deep JavaScript Dr. Axel Rauschmayer 2020
Copyright © 2020 by Dr. Axel Rauschmayer Cover photo by Jakob Boman on Unsplash All rights reserved. This book or any portion thereof may not be reproduced or used in any manner whatsoever without the express written permission of the publisher except for the use of brief quotations in a book review or scholarly journal. exploringjs.com
Contents I Frontmatter 7 1 About this book 9 1.1 Where is the homepage of this book? . . . . . . . . . . . . . . . . . . . . 9 1.2 What is in this book? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.3 What do I get for my money? . . . . . . . . . . . . . . . . . . . . . . . . 10 1.4 How can I preview the content? . . . . . . . . . . . . . . . . . . . . . . . 10 1.5 How do I report errors? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.6 Tips for reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.7 Notations and conventions . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.8 Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 II Types, values, variables 13 2 Type coercion in JavaScript 15 2.1 What is type coercion? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.2 Operations that help implement coercion in the ECMAScript specification 18 2.3 Intermission: expressing specification algorithms in JavaScript . . . . . . 20 2.4 Example coercion algorithms . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.5 Operations that coerce . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 2.6 Glossary: terms related to type conversion . . . . . . . . . . . . . . . . . 32 3 The destructuring algorithm 33 3.1 Preparing for the pattern matching algorithm . . . . . . . . . . . . . . . 33 3.2 The pattern matching algorithm . . . . . . . . . . . . . . . . . . . . . . . 35 3.3 Empty object patterns and Array patterns . . . . . . . . . . . . . . . . . . 37 3.4 Applying the algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 4 A detailed look at global variables 41 4.1 Scopes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 4.2 Lexical environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 4.3 The global object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 4.4 In browsers, globalThis does not point directly to the global object . . . 42 4.5 The global environment . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 4.6 Conclusion: Why does JavaScript have both normal global variables and the global object? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 4.7 Further reading and sources of this chapter . . . . . . . . . . . . . . . . . 47 3
4 CONTENTS 5 % is a remainder operator, not a modulo operator (bonus) 49 5.1 Remainder operator rem vs. modulo operator mod . . . . . . . . . . . . . 49 5.2 An intuitive understanding of the remainder operation . . . . . . . . . . 50 5.3 An intuitive understanding of the modulo operation . . . . . . . . . . . . 50 5.4 Similarities and differences between rem and mod . . . . . . . . . . . . . . 51 5.5 The equations behind remainder and modulo . . . . . . . . . . . . . . . 51 5.6 Where are rem and mod used in programming languages? . . . . . . . . . 53 5.7 Further reading and sources of this chapter . . . . . . . . . . . . . . . . . 54 III Working with data 55 6 Copying objects and Arrays 57 6.1 Shallow copying vs. deep copying . . . . . . . . . . . . . . . . . . . . . . 57 6.2 Shallow copying in JavaScript . . . . . . . . . . . . . . . . . . . . . . . . 58 6.3 Deep copying in JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . 62 6.4 Further reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 7 Updating data destructively and non-destructively 67 7.1 Examples: updating an object destructively and non-destructively . . . . 67 7.2 Examples: updating an Array destructively and non-destructively . . . . 68 7.3 Manual deep updating . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 7.4 Implementing generic deep updating . . . . . . . . . . . . . . . . . . . . 69 8 The problems of shared mutable state and how to avoid them 71 8.1 What is shared mutable state and why is it problematic? . . . . . . . . . . 71 8.2 Avoiding sharing by copying data . . . . . . . . . . . . . . . . . . . . . . 73 8.3 Avoiding mutations by updating non-destructively . . . . . . . . . . . . 75 8.4 Preventing mutations by making data immutable . . . . . . . . . . . . . 76 8.5 Libraries for avoiding shared mutable state . . . . . . . . . . . . . . . . . 76 IV OOP: object property attributes 79 9 Property attributes: an introduction 81 9.1 The structure of objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 9.2 Property descriptors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 9.3 Retrieving descriptors for properties . . . . . . . . . . . . . . . . . . . . 85 9.4 Defining properties via descriptors . . . . . . . . . . . . . . . . . . . . . 86 9.5 Object.create(): Creating objects via descriptors . . . . . . . . . . . . . 88 9.6 Use cases for Object.getOwnPropertyDescriptors() . . . . . . . . . . . 89 9.7 Omitting descriptor properties . . . . . . . . . . . . . . . . . . . . . . . . 91 9.8 What property attributes do built-in constructs use? . . . . . . . . . . . . 92 9.9 API: property descriptors . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 9.10 Further reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 10 Protecting objects from being changed 99 10.1 Levels of protection: preventing extensions, sealing, freezing . . . . . . . 99 10.2 Preventing extensions of objects . . . . . . . . . . . . . . . . . . . . . . . 100 10.3 Sealing objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
CONTENTS 5 10.4 Freezing objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 10.5 Further reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 11 Properties: assignment vs. definition 105 11.1 Assignment vs. definition . . . . . . . . . . . . . . . . . . . . . . . . . . 106 11.2 Assignment and definition in theory (optional) . . . . . . . . . . . . . . . 106 11.3 Definition and assignment in practice . . . . . . . . . . . . . . . . . . . . 110 11.4 Which language constructs use definition, which assignment? . . . . . . 114 11.5 Further reading and sources of this chapter . . . . . . . . . . . . . . . . . 115 12 Enumerability of properties 117 12.1 How enumerability affects property-iterating constructs . . . . . . . . . . 117 12.2 The enumerability of pre-defined and created properties . . . . . . . . . 122 12.3 Use cases for enumerability . . . . . . . . . . . . . . . . . . . . . . . . . 122 12.4 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 V OOP: techniques 129 13 Techniques for instantiating classes 131 13.1 The problem: initializing a property asynchronously . . . . . . . . . . . . 131 13.2 Solution: Promise-based constructor . . . . . . . . . . . . . . . . . . . . 132 13.3 Solution: static factory method . . . . . . . . . . . . . . . . . . . . . . . . 133 13.4 Subclassing a Promise-based constructor (optional) . . . . . . . . . . . . 137 13.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 13.6 Further reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 14 Copying instances of classes: .clone() vs. copy constructors 141 14.1 .clone()methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 14.2 Static factory methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 14.3 Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 15 Immutable wrappers for collections 145 15.1 Wrapping objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 15.2 An immutable wrapper for Maps . . . . . . . . . . . . . . . . . . . . . . 146 15.3 An immutable wrapper for Arrays . . . . . . . . . . . . . . . . . . . . . 147 VI Regular expressions 149 16 Regular expressions: lookaround assertions by example 151 16.1 Cheat sheet: lookaround assertions . . . . . . . . . . . . . . . . . . . . . 151 16.2 Warnings for this chapter . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 16.3 Example: Specifying what comes before or after a match (positive lookaround) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 16.4 Example: Specifyingwhat does not come before or after amatch (negative lookaround) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 16.5 Interlude: pointing lookaround assertions inward . . . . . . . . . . . . . 154 16.6 Example: match strings not starting with 'abc' . . . . . . . . . . . . . . 154 16.7 Example: match substrings that do not contain '.mjs' . . . . . . . . . . . 155
6 CONTENTS 16.8 Example: skipping lines with comments . . . . . . . . . . . . . . . . . . 155 16.9 Example: smart quotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 16.10Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 16.11Further reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 17 Composing regular expressions via re-template-tag (bonus) 159 17.1 The basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 17.2 A tour of the features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 17.3 Why is this useful? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 17.4 re and named capture groups . . . . . . . . . . . . . . . . . . . . . . . . 160 VII Miscellaneous topics 163 18 Exploring Promises by implementing them 165 18.1 Refresher: states of Promises . . . . . . . . . . . . . . . . . . . . . . . . . 166 18.2 Version 1: Stand-alone Promise . . . . . . . . . . . . . . . . . . . . . . . 166 18.3 Version 2: Chaining .then() calls . . . . . . . . . . . . . . . . . . . . . . 169 18.4 Convenience method .catch() . . . . . . . . . . . . . . . . . . . . . . . 170 18.5 Omitting reactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 18.6 The implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 18.7 Version 3: Flattening Promises returned from .then() callbacks . . . . . 172 18.8 Version 4: Exceptions thrown in reaction callbacks . . . . . . . . . . . . . 175 18.9 Version 5: Revealing constructor pattern . . . . . . . . . . . . . . . . . . 177 19 Metaprogramming with Proxies (early access) 179 19.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 19.2 Programming versus metaprogramming . . . . . . . . . . . . . . . . . . 181 19.3 Proxies explained . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 19.4 Use cases for Proxies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 19.5 The design of the Proxy API . . . . . . . . . . . . . . . . . . . . . . . . . 200 19.6 FAQ: Proxies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 19.7 Reference: the Proxy API . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 19.8 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 19.9 Further reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 20 The property .name of functions (bonus) 217 20.1 Names of functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 20.2 Constructs that provide names for functions . . . . . . . . . . . . . . . . 219 20.3 Things to look out for with names of functions . . . . . . . . . . . . . . . 223 20.4 Changing the names of functions . . . . . . . . . . . . . . . . . . . . . . 223 20.5 The function property .name in the ECMAScript specification . . . . . . . 224
(This page has no text content)
Chapter 1 About this book Contents 1.1 Where is the homepage of this book? . . . . . . . . . . . . . . . . . 9 1.2 What is in this book? . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.3 What do I get for my money? . . . . . . . . . . . . . . . . . . . . . . 10 1.4 How can I preview the content? . . . . . . . . . . . . . . . . . . . . . 10 1.5 How do I report errors? . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.6 Tips for reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.7 Notations and conventions . . . . . . . . . . . . . . . . . . . . . . . 10 1.7.1 What is a type signature? Why am I seeing static types in this book? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.7.2 What do the notes with icons mean? . . . . . . . . . . . . . . . 11 1.8 Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.1 Where is the homepage of this book? The homepage of “Deep JavaScript” is exploringjs.com/deep-js/ 1.2 What is in this book? This book dives deeply into JavaScript: • It teaches practical techniques for using the language better. • It teaches how the language works and why. What it teaches is firmly grounded in the ECMAScript specification (which the book explains and refers to). • It covers only the language (ignoring platform-specific features such as browser APIs) but not exhaustively. Instead, it focuses on a selection of important topics. 9
10 1 About this book 1.3 What do I get for my money? If you buy this book, you get: • The current content in four DRM-free versions: – PDF file – ZIP archive with ad-free HTML – EPUB file – MOBI file • Any future content that is added to this edition. Howmuch I can add depends on the sales of this book. The current price is introductory. It will increase as more content is added. 1.4 How can I preview the content? On the homepage of this book, there are extensive previews for all versions of this book. 1.5 How do I report errors? • The HTML version of this book has a link to comments at the end of each chapter. • They jump to GitHub issues, which you can also access directly. 1.6 Tips for reading • You can read the chapters in any order. Each one is self-contained but occasionally, there are references to other chapters with further information. • The headings of some sections are marked with “(optional)” meaning that they are non-essential. You will still understand the remainders of their chapters if you skip them. 1.7 Notations and conventions 1.7.1 What is a type signature? Why am I seeing static types in this book? For example, you may see: Number.isFinite(num: number): boolean That is called the type signature of Number.isFinite(). This notation, especially the static types number of num and boolean of the result, are not real JavaScript. The notation is borrowed from the compile-to-JavaScript language TypeScript (which is mostly just JavaScript plus static typing). Why is this notation being used? It helps give you a quick idea of how a function works. The notation is explained in detail in a 2ality blog post, but is usually relatively intuitive.
1.8 Acknowledgements 11 1.7.2 What do the notes with icons mean? Reading instructions Explains how to best read the content. External content Points to additional, external, content. Tip Gives a tip related to the current content. Question Asks and answers a question pertinent to the current content (think FAQ). Warning Warns about pitfalls, etc. Details Provides additional details, complementing the current content. It is similar to a footnote. 1.8 Acknowledgements • Thanks to Allen Wirfs-Brock for his advice via Twitter and blog post comments. It helped make this book better. • More people who contributed are acknowledged in the chapters.
Part II Types, values, variables 13
(This page has no text content)
Chapter 2 Type coercion in JavaScript Contents 2.1 What is type coercion? . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.1.1 Dealing with type coercion . . . . . . . . . . . . . . . . . . . . 17 2.2 Operations that help implement coercion in the ECMAScript speci- fication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.2.1 Converting to primitive types and objects . . . . . . . . . . . . 18 2.2.2 Converting to numeric types . . . . . . . . . . . . . . . . . . . 18 2.2.3 Converting to property keys . . . . . . . . . . . . . . . . . . . 19 2.2.4 Converting to Array indices . . . . . . . . . . . . . . . . . . . 19 2.2.5 Converting to Typed Array elements . . . . . . . . . . . . . . 20 2.3 Intermission: expressing specification algorithms in JavaScript . . . 20 2.4 Example coercion algorithms . . . . . . . . . . . . . . . . . . . . . . 21 2.4.1 ToPrimitive() . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.4.2 ToString() and related operations . . . . . . . . . . . . . . . 24 2.4.3 ToPropertyKey() . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.4.4 ToNumeric() and related operations . . . . . . . . . . . . . . . 28 2.5 Operations that coerce . . . . . . . . . . . . . . . . . . . . . . . . . . 29 2.5.1 Addition operator (+) . . . . . . . . . . . . . . . . . . . . . . . 29 2.5.2 Abstract Equality Comparison (==) . . . . . . . . . . . . . . . 30 2.6 Glossary: terms related to type conversion . . . . . . . . . . . . . . 32 In this chapter, we examine the role of type coercion in JavaScript. We will go relatively deeply into this subject and, e.g., look into how the ECMAScript specification handles coercion. 2.1 What is type coercion? Each operation (function, operator, etc.) expects its parameters to have certain types. If a value doesn’t have the right type for a parameter, three common options for, e.g., a function are: 15
16 2 Type coercion in JavaScript 1. The function can throw an exception: function multiply(x, y) { if (typeof x !== 'number' || typeof y !== 'number') { throw new TypeError(); } // ··· } 2. The function can return an error value: function multiply(x, y) { if (typeof x !== 'number' || typeof y !== 'number') { return NaN; } // ··· } 3. The function can convert its arguments to useful values: function multiply(x, y) { if (typeof x !== 'number') { x = Number(x); } if (typeof y !== 'number') { y = Number(y); } // ··· } In (3), the operation performs an implicit type conversion. That is called type coercion. JavaScript initially didn’t have exceptions, which is why it uses coercion and error values for most of its operations: // Coercion assert.equal(3 * true, 3); // Error values assert.equal(1 / 0, Infinity); assert.equal(Number('xyz'), NaN); However, there are also cases (especially when it comes to newer features) where it throws exceptions if an argument doesn’t have the right type: • Accessing properties of null or undefined: > undefined.prop TypeError: Cannot read property 'prop' of undefined > null.prop TypeError: Cannot read property 'prop' of null > 'prop' in null TypeError: Cannot use 'in' operator to search for 'prop' in null
2.1 What is type coercion? 17 • Using symbols: > 6 / Symbol() TypeError: Cannot convert a Symbol value to a number • Mixing bigints and numbers: > 6 / 3n TypeError: Cannot mix BigInt and other types • New-calling or function-calling values that don’t support that operation: > 123() TypeError: 123 is not a function > (class {})() TypeError: Class constructor cannot be invoked without 'new' > new 123 TypeError: 123 is not a constructor > new (() => {}) TypeError: (intermediate value) is not a constructor • Changing read-only properties (only throws in strict mode): > 'abc'.length = 1 TypeError: Cannot assign to read only property 'length' > Object.freeze({prop:3}).prop = 1 TypeError: Cannot assign to read only property 'prop' 2.1.1 Dealing with type coercion Two common ways of dealing with coercion are: • A caller can explicitly convert values so that they have the right types. For example, in the following interaction, we want to multiply two numbers encoded as strings: let x = '3'; let y = '2'; assert.equal(Number(x) * Number(y), 6); • A caller can let the operation make the conversion for them: let x = '3'; let y = '2'; assert.equal(x * y, 6); I usually prefer the former, because it clarifies my intention: I expect x and y not to be numbers, but want to multiply two numbers.
18 2 Type coercion in JavaScript 2.2 Operations that help implement coercion in the ECMAScript specification The following sections describe themost important internal functions used by the ECMA- Script specification to convert actual parameters to expected types. For example, in TypeScript, we would write: function isNaN(number: number) { // ··· } In the specification, this looks as follows (translated to JavaScript, so that it is easier to understand): function isNaN(number) { let num = ToNumber(number); // ··· } 2.2.1 Converting to primitive types and objects Whenever primitive types or objects are expected, the following conversion functions are used: • ToBoolean() • ToNumber() • ToBigInt() • ToString() • ToObject() These internal functions have analogs in JavaScript that are very similar: > Boolean(0) false > Boolean(1) true > Number('123') 123 After the introduction of bigints, which exists alongside numbers, the specification often uses ToNumeric() where it previously used ToNumber(). Read on for more information. 2.2.2 Converting to numeric types At the moment, JavaScript has two built-in numeric types: number and bigint. • ToNumeric() returns a numeric value num. Its callers usually invoke amethod mthd of the specification type of num: Type(num)::mthd(···)
Comments 0
Loading comments...
Reply to Comment
Edit Comment