This page contains my notes from the following books:
a) Javascript - The Definitive Guide by David Flanagan

Index for JS The Definitive Guide book:
Chapter 2 - Lexical Structure
Chapter 3 - Types, Values, and Variables
Chapter 4 - Expressions and Operators
Chapter 5 - Statements
Chapter 6 - Objects

Chapter 2: Lexical Structure

Identifiers and Reserved Words
- An identifier is simply a name.
- In JS, identifiers are used to name constants, variables, properties, functions, and classes and to provide labels for certain loops in JS code.
- A JS identifier must begin with a letter, an underscore, or a dollar sign. Digits are not allowed to be the first character.
- Certain reserved words like if, while, for cannot be used as the name of constants, variables, functions, or classes. But they can be used as names of properties within an object.
- Others such as from, of, get, set are used in limited contexts with no syntactic ambiguity and are perfectly legal as identifiers.
- Others like let can't be fully reserved in order to retain backward compatibility with older programs. let can be used as a variable name if declared with var outside of a class, for example, but not if declared inside a class or with const.
Some background about Unicode
- The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!).
- The only characters that mattered were good old unaccented English letters, and we had a code for them called ASCII which was able to represent every character using a number between 32 and 127. Space was 32, the letter "A" was 65, etc. This could conveniently be stored in 7 bits. Most computers in those days were using 8-bit bytes, so not only could you store every possible ASCII character, but you had a whole bit to spare.
- In the ANSI standard, everybody agreed on what to do below 128, which was pretty much the same as ASCII, but there were lots of different ways to handle the characters from 128 and on up, depending on where you lived. These different systems were called code pages. But still, most people just pretended that a byte was a character and a character was 8 bits and as long as you never moved a string from one computer to another, or spoke more than one language, it would sort of always work. Luckily, Unicode had been invented.
- Some people are under the misconception that Unicode is simply a 16-bit code where each character takes 16 bits and therefore there are 65,536 possible characters. This is not, actually, correct. It is the single most common myth about Unicode. In fact, Unicode has a different way of thinking about characters, and you have to understand the Unicode way of thinking of things or nothing will make sense.
- Until now, we've assumed that a letter maps to some bits which you can store on disk or in memory: A -> 0100 0001.
- In Unicode, a letter maps to something called a code point which is still just a theoretical concept. How that code point is represented in memory or on disk is a whole another story.
- Consider the letter A. Every (platonic, as in unique) letter in every alphabet is assigned a magic number by the Unicode consortium which is written like this: U+0041. You can find them all by visiting the Unicode website. There is no real limit on the number of letters that Unicode can define and in fact they have gone beyond 65,536 so not every unicode letter can really be squeezed into two bytes, but that was a myth anyway.
- OK, so say we have a string: Hello which, in Unicode, corresponds to these five code points: U+0048 U+0065 U+006C U+006C U+006F. Just a bunch of code points. Numbers, really. We haven't yet said anything about how to store this in memory or represent it in an email message. That's where encodings come in.
- Unicode Transformation Format 8-bit (UTF-8) is a variable-width encoding that can represent every character in the Unicode character set. It was designed for backward compatibility with ASCII and to avoid the complications of endianness and byte order marks in UTF-16 and UTF-32. UTF-8 is capable of encoding all 1,112,064 valid Unicode code points using one to four one-byte (8-bit) code units. What the variable-width means is that characters with code points with small values (like A) can be represented with just one byte. In fact, in UTF-8, every code point from 0-127 is stored in a single byte. Only code points 128 and above are stored using 2, 3, up to 6 bytes (Note: using five- and six-octet sequences are now illegal UTF-8). This enables UTF-8 to be backwards compatible with ASCII.
- For example, Hello, which was U+0048 U+0065 U+006C U+006C U+006F, will be stored as 48 65 6C 6C 6F, which, behold! is the same as it was stored in ASCII, and ANSI, and every OEM character set on the planet. Now, if you are so bold as to use accented letters or Greek letters or Klingon letters, you'll have to use several bytes to store a single code point.
- UTF-8 on wikipedia explains with an example how the encoding is actually done. Point 3 has the sentence: "The two leading zeros are added because a three-byte encoding needs exactly sixteen bits from the code point." What this means is that the number of x's that we need to fill in the U+0800 to U+FFFF encoding is 16.
- Note: A "character" can take more than 4 bytes because it is made of more than one code point. For instance a national flag character takes 8 bytes since it is "constructed from a pair of Unicode scalar values" both from outside the Base Multilingual Plane. (I think what this means is that each of the two code points that would combine to form a flag representation won't individually be the same as the code point that maps to an already existing platonic letter. This way when the parser sees this code point, it knows that it has to treat the two code points as a "single character" and thus render it as expected as a flag. And not as two disjoint platonic letters.) I think this understanding is correct as this text on Wikipedia explains. Since the ranges for the high surrogates (0xD800–0xDBFF), low surrogates (0xDC00–0xDFFF), and valid BMP characters (0x0000–0xD7FF, 0xE000–0xFFFF) are disjoint, it is not possible for a surrogate to match a BMP character, or for two adjacent code units to look like a legal surrogate pair. This simplifies searches a great deal. (But note that surrogate pair is a UTF-16 concept, and does not apply to UTF-8 (?)).
- Note: Some complex emoji characters can take even more than this; the transgender flag emoji (πŸ³οΈβ€βš§οΈ), which consists of the five-codepoint sequence U+1F3F3 U+FE0F U+200D U+26A7 U+FE0F, requires sixteen bytes to encode, while that for the flag of Scotland (🏴󠁧󠁒󠁳󠁣󠁴󠁿) requires a total of twenty-eight bytes for the seven-codepoint sequence U+1F3F4 U+E0067 U+E0062 U+E0073 U+E0063 U+E0074 U+E007F. Then there's Zalgo text.
- Uh oh: JavaScript has a Unicode problem.
- What's the difference between a character, a code point, a glyph and a grapheme?
- From the wikipedia definitions:
a) Code Units: The code unit size is equivalent to the bit measurement for the particular encoding:
- A code unit in US-ASCII consists of 7 bits
- A code unit in UTF-8, EBCDIC and GB 18030 consists of 8 bits
- A code unit in UTF-16 consists of 16 bits
- A code unit in UTF-32 consists of 32 bits
b) Code Points: A code point is represented by a sequence of code units. The mapping is defined by the encoding. Thus, the number of code units required to represent a code point depends on the encoding:
- UTF-8: code points map to a sequence of one, two, three or four code units.
- UTF-16: code units are twice as long as 8-bit code units. Therefore, any code point with a scalar value less than U+10000 is encoded with a single code unit. Code points with a value U+10000 or higher require two code units each. These pairs of code units have a unique term in UTF-16: "Unicode surrogate pairs". (What this means is that Surrogate Pairs are a term that is limited to UTF-16)
UTF-32: the 32-bit code unit is large enough that every code point is represented as a single code unit.
-

The Single Most Important Fact About Encodings is

"It does not make sense to have a string without knowing what encoding it uses." How do we preserve this information about what encoding a string uses? When it comes to html, use the following <meta http-equiv="Content-Type" content="text/html; charset=utf-8">. But that meta tag really has to be the very first thing in the <head> section because as soon as the web browser sees this tag it’s going to stop parsing the page and start over after reinterpreting the whole page using the encoding you specified.
- codepoints.net helps you find the character corresponding to a particular code point.
Unicode escapes sequences when it comes to JS
- JS defines escapes sequences that allows us to write Unicode characters using only ASCII characters.
- These Unicode escapes begin with the characters \u and are either followed by exactly four hexadecimal digits (uisng uppercase or lowercase letters A-F) or by one to six hexadecimal digits enclosed within curly braces.
Expand Gist Expand Code Snippet
- Note that Unicode allows more than one way of encoding the same character. The string 'Γ©' for example, can be encoded as the single Unicode character \u00E9 or as the regular ASCII 'e' followed by the acute accent combining mark \u0301.
- The Unicode standard defines the preferred encoding for all characters and specifies a normalization procedure to convert text to a canonical form suitable for comparisons. JavaScript assumes that the source code it is interpreting has already been normalized and does not do any normalization on its own. If you plan to use Unicode characters in your JavaScript programs, you should ensure that your editor or some other tool performs Unicode normalization of your source code to prevent you from ending up with different but visually indistinguishable identifiers.
Expand Gist Expand Code Snippet

Chapter 3: Types, Values, and Variables

Overview and Definitions
- JS types can be divided into two categories: primitive tyeps and object types. Primitive values include numbers, strings, and booleans.
- The special JS values null and undefined are primitive values but they are not numbers, strings, or booleans.
- ES6 adds a new special-purpose type known as Symbol.
- Any JS value that is not a number, string, boolean, symbol, null, or undefined is an object. An object is a collection of properties where each property has a name and a value (the value can be a primitive value or another object).
- One very special object is the global object.
- JS supports object-oriented programming style. Loosely, this means that rather than having globally defined functions to operate on values of various types, the types themselves define methods for working with values. For instance, to sort the elements of an array a, we don't pass a to a sort() function, instead we invoke the sort method on a as a.sort().
- Technically it is only JS objects that have methods. But numbers, strings, boolean, and symbol values behave as if they have methods. In JS, null and undefined are the only values that methods cannot be invoked on.
- JS's object types are mutable and its primitive types are immutable.
- JS liberally converts values from one type to another based on some rules. These rules affect its definition of equality, and the == operator performs type conversion along with comparison. In practice, however, the == operator is deprecated in favor of the strict equality operator === which does no type conversions.
- JS constants and variables are untyped: declarations do not specify what kind of values will be assigned.
Numbers
- JavaScript represents numbers using the 64-bit floating-point format defined by the IEEE 754 standard, which means it can represent numbers as large as Β±1.7976931348623157 Γ— 10308 and as small as Β±5 Γ— 10-324. IEEE 754 standard is the format for numbers of type double in Java, C++, and most modern programming languages. So the point to note is that the same datatype is being used to represent both integers as well as floating-point numbers. The mental picture of having separate representation for int and double / float like in Java is incorrect.
- The JavaScript number format allows you to exactly represent all integers between βˆ’9,007,199,254,740,992 (βˆ’253) and 9,007,199,254,740,992 (253), inclusive. If you use integer values larger than this, you may lose precision in the trailing digits.
- Note however that certain operations in JS such as array indexing and the bitwise operators are performed with 32-bit integers. What this means is that the maximum number of elements that you can store in an array is 232 - 1 = 4,294,967,295.
Expand Gist Expand Code Snippet
- ES2016 adds ** for exponentiation. There are functions defined on the Math Object. The Math namespace object contains static properties and methods for mathematical constants and functions. Math works with the Number type. It doesn't work with BigInt.
- Arithmetic in JS does not raise errors in cases of overflow, underflow, or division by zero.
- Overflow occurs when the result of a numeric operation is larger than the largest representable number. In this case, the result is a special infinity value, Infinity. Similarly, when the absolute value of a negative value becomes larger than the absolute value of the largest representable negative number, the result is negative infinity, -Infinity.
- Underflow occurs when the result of a numeric operation is closer to zero than the smallest representable number. In this case, JS returns 0. If underflow occurs from a negative number, JS returns a special value known as "negative zero." This value is almost completely indistinguishable from regular zero and JavaScript programmers rarely need to detect it.
- Any ways, here is a SO post answering if +0 and -0 are the same? Note that +0 === -0 evaluates to true but Object.is(-0, +0) evaluates to false.
- Division by zero is not an error in JS, it returns infinity or negative infinity.
- There is one exception, however: zero divided by zero does not have a well-defined value, and the result of this operation is the special not-a-number value NaN. NaN also arises if you attempt to divide infinity by infinity, take the square root of a negative number, or use arithmetic operators with non-numeric operands that cannot be converted to numbers.
- The not-a-number value has one unusual feature in JavaScript: it does not compare equal to any other value, including itself. This means that you can't write x === NaN to determine whether the value of a variable x is NaN. Instead, you must write x != x or Number.isNaN(x). Those expressions will be true if, and only if, x has the same value as the global constant NaN. Note that there is a similar method isNan() defined on the global object. You can use either of these.
- A related function Number.isFinite() returns true if its argument is a number other than NaN, Infinity, or -Infinity.
- BigInt is a numeric type whose values are integers. The type was added to JavaScript mainly to allow the representation of 64-bit integers, which are required for compatibility with many other programming languages. BigInt literals are written as a string of digits followed by a lowercase letter n. You can use BigInt() as a function for converting regular JavaScript numbers or strings to BigInt values. You cannot mix operands of type BigInt with regular numbers. Bitwise operators "generally" work with BigInt operands. None of the functions of the Math object accept BigInt operands however.
Dates
- JS defines a Date object that encapsulates an integral number that represents milliseconds since the midnight at the beginning of January 1, 1970, UTC (the epoch).
Expand Gist Expand Code Snippet
Text
- A string is an immutable ordered sequence of 16-bit values, each of which typically represents a Unicode character.
- The length of a string is the number of 16-bit values it contains.
- JS uses UTF-16 encoding of the Unicode character set. The most commonly used Unicode characters (those from the "Basic Multilingual Plane") have codepoints that fit in 16 bits and can be represented by one element of a string. Unicode characters whose codepoints do not fit in 16 bits are encoded using the rules of UTF-16 as a sequence (known as a "surrogate-pair") of two 16-bit values. This means that a JS string of length 2 (two 16-bit values) might represent only a single Unicode character.
- Most string-manipulation methods defined by JavaScript operate on 16-bit values, not characters. They do not treat surrogate pairs specially, they perform no normalization of the string, and don't even ensure that a string is well-formed UTF-16. In ES6, however, strings are iterable, and if you use the for/of loop or ... operator with a string, it will iterate the actual characters of the string, not the 16-bit values.
Expand Gist Expand Code Snippet
- Some operations that can be performed with a String:
Expand Gist Expand Code Snippet
Template Literals
- Template literals can include arbitrary JS expressions.
- Everything between the ${...} is interpreted as a JS expression.
- Another feature of template literals is that if a function name (or "tag") comes right before the opening backtick, then the text and the values of the expressions within the template literal are passed to the function.
- TODO: Section also goes over Regular Expressions Regex.
Expand Gist Expand Code Snippet
Boolean Values
- Any JS value can be converted to a boolean value.
- The following values convert to, and therefore work like, false:
a) undefined
b) null
c) 0
d) -0
f) "" (the empty string)
- All other values including all objects and arrays convert to, and work like, true.
- For example, you can test explicitly to see if an object obj is not null by doing if (obj !== null) {...}
Expand Gist Expand Code Snippet
null and undefined
- Using the typeof operator on null returns the string "object", indicating that null can be thought of as a special object value that indicates "no object".
- JS also has a second value that indicates absence of value. The undefined value represents a deeper kind of absence. It is the value of variables that have not been initialized and the value you get when you query the vaalue of an object property or array element that does not exist.
- undefined is also the return value of functions that do not explicitly return a value and the value of function parameters for which no value has been passed.
Symbols
- JS's fundamental Object type is an unordered collection of properties, where each property has a name and a value. Property names, until ES6, were exclusively strings. But in ES6 and later, Symbols can also serve this purpose.
- To obtain a Symbol value, you call the Symbol() function. This function never returns the same value twice, even when called with the same argument.
- If you supply a string argument, that string will be included in the output of the Symbol's toString() method. Note in the below example that calling Symbol() twice with the same stirng produces two completely different Symbol values.
- Symbol() never returns the same value twice, but Symbol.for() always returns the same value when called with the same string.
Expand Gist Expand Code Snippet
The Global Object
- The global object is a regular JS object that serves a very important purpose: the properties of this object are the globally defined identifiers that are available to a JS program. When the JS interpreter starts or whenever a web browser loads a new page, it creates a new global object and gives it an initial set of properties that define:
a) Global constants like undefined, Infinity, and NaN
b) Global functions like isName(), parseInt(), and eval()
c) Constructor functions llke Date(), RegExp(), String(), Object(), Array()
d) Global objects like Math and JSON
- In browsers, the Window object serves as the global object for all JS code contained in the browser window it represents. This global Window object has a self-referential window property that can be used to refer to the global object. The Window object defines the core global properties, but it also defines quite a few other globals that are specific to web browsers and client-side JS.
- ES2020 defines globalThis as the standard way to refer to the global object in any context (like Node / Web browser / Web Worker Thread).
- From window docs on MDN.
-

Note in the below example that global variables declared with var become the properties of the window object. This is not the case when you declare variables with let or const.

We will see more about global variables and how defining them means that they can be accessed across all <script> tags that are associated with that HTML file in "Variable and Constant Scope" section somewhere below.
Expand Gist Expand Code Snippet
Type Conversions
- The below table summarizes how values convert from one type to another in JS. Bold entries in the table highlight conversions that you may find surprising. Empty cells indicate that no conversion is necessary and none is performed.
Show Image Show Image Button
Example1
- JS has two operators that test whether two values are equal. The "strict equality operator" === does not consider its operands to be equal if they are not of the same type.
- Because JS is flexible with type conversions, it also defines the == operator with a flexible definition of equality.
- Keep in mind that convertibility of one value to another does not imply equality of those two values.
Expand Gist Expand Code Snippet
- Of course we can always perform explicit type conversions when required.
- The simplest way to perform a type conversion is to use the Boolean(), Number(), and String() functions.
- Certain JS operators perform implicit type conversions and are sometimes used explicitly for the purpose of type conversion. If one operand of the + operator is a string, it converts the other one to a string. The unary + operator converts its operand to a number. And the unary ! operator converts its operand to a boolean and negates it.
- When it comes to converting strings to numbers, the parseInt() and parseFloat() offer more flexibility.
Expand Gist Expand Code Snippet
- Sometimes you might have to convert a number to string and you might want to control the number of decimal points that are present in the formatted string. This, and other such operations, can be done using the following methods.
Expand Gist Expand Code Snippet
Object to Primitive Conversions
- // TODO: If this is your first reading of this chapter, you should feel free to skip ahead to Β§3.10.
Variable declaration and assignment
- In modern JS (ES6 and later), variables are declared with the let keyword. It is good programming practice to assign an initial value to your variables when you declare them when possible.
- If you don't specify an initial value for a variable with the let statement, the variable is declared, but its value is undefined until your code assigns a value to it.
- To declare a constant, use const. const works just like let except that you must initialize the constant when you declare it.
Expand Gist Expand Code Snippet
Variable and Constant Scope
- The scope of a variable is the region of your program source code in which it is defined.
- Variables and constants declared with let and const are block scoped. This means that they are only defined within the block of code in which the let or const statement appears.
- When a declaration (with any of let, const, or var) appears at the top level, outside of any code blocks, we say it is a global variable or constant and has global scope.
- In Node and in client-side JS modules (Chapter 10), the scope of a global variable is the file that it is defined in.
- In traditional client-side JS, however, the scope of a global variable is the HTML document in which it is defined.
-

What this is means is that if one <script> declares a global variable or constant, that variable or constant is defined (and can be accessed ) in all of the <script> elements in that HTML document (or at least all of the scripts that execute after the let or const statement executes).

Show Image Show Image Button
Example1
Difference between var and let
- These differences are more for reference. Pretty much a copy-paste of the section in the book.
- Variables declared with var do not have block scope. Instead, they are scoped to the body of the containing function no matter how deeply nested they are inside that function.
- If you use var outside of a function body, it declares a global variable. But global variables declared with var differ from globals declared with let in an important way. Globals declared with var are implemented as properties of the global object. The global object can be referenced as globalThis. So if you write var x = 2; outside of a function, it is like you wrote globalThis.x = 2;. Note however, that the analogy is not perfect: the properties created with global var declarations cannot be deleted with the delete operator. Global variables and constants declared with let and const are not properties of the global object.
- Unlike variables declared with let, it is legal to declare the same variable multiple times with var.
- When a variable is declared with var, the declaration is lifted up (or "hoisted") to the top of the enclosing function. The initialization of the variable remains where you wrote it, but the definition of the variable moves to the top of the function. So variables declared with var can be used, without error, anywhere in the enclosing function. If the initialization code has not run yet, then the value of the variable may be undefined, but you won't get an error if you use the variable before it is initialized. This can be a source of bugs and is one of the important misfeatures that let corrects: if you declare a variable with let but attempt to use it before the let statement runs, you will get an actual error instead of just seeing an undefined value.
- In strict mode, if you attempt to use an undeclared variable, you'll get a reference error when you run your code. Outside of strict mode, however, if you assign a value to a name that has not been declared with let, const, or var, you'll end up creating a new global variable. It will be a global no matter how deeply nested within functions and blocks your code is, which is almost certainly not what you want, is bug-prone, and is one of the best reasons for using strict mode! Global variables created in this accidental way are like global variables declared with var: they define properties of the global object. But unlike the properties defined by proper var declarations, these properties can be deleted with the delete operator.
Destructuring Assignment
- In a destructuring assignment, the value on the right hand side of the equals sign is an array or object (a "structured" value) and the left hand side specifies one or more variable names using a syntax that mimics array and object literal syntax.
Expand Gist Expand Code Snippet

Chapter 4: Expressions and Operators

Object and Array Initializers
- Object and array initializers are expressions whose value is a newly created object or array.
- An array initializer is a comma-separated list of expressions contained within square brackets.
Expand Gist Expand Code Snippet
- An object initializer expression is like an array initializer expression, but the square brackets are replaced by curly brackets, and each subexpression is prefixed with a property name and a colon.
Expand Gist Expand Code Snippet
Property Access Expressions
- A property access expression evaluates to the value of an object property or an array element.
- You can access properties either using expression . identifier or expression [expression] .
- The difference between the two is that: when using a dot and an identifier, the value of the property name by that identifier is looked up. On the other hand, if [] is used, the expression inside the [] is evaluated and converted into a string, and then the value of the property named by that string is looked up. Using the dot notation is the simpler way, but you can use it only when you already know the name of the property (while writing the program) that you want to access. If the property contains spaces, or punctuations, or itself is the result of some computation, you will need to use the [] syntax instead.
- As we have seen before, two JS values cannot have properties: null and undefined. Trying to access properties on either of these, either through . or [] syntax, causes a TypeError to be thrown.
Expand Gist Expand Code Snippet
Conditional Property Access
- ES2020 adds two new kinds of property access expressions: expression ?. identifier and expression ?. [expression].
- These are used to guard against the TypeError that we get when trying to access properties on null or undefined.
- Consider the expression a?.b.

If a is null or undefined, then the expression evaluates to undefined

without any attempt to access the property b. If a is some other valid value, then a?.b evaluates to whatever a.b would evaluate to. And if a does not have a property named b, then the value will again be undefined.
- Consider the below example. a is an object, so a.b is a valid property access expression. But the value of a.b is null, so a.b.c would throw a TypeError. By using ?. instead of . we avoid the TypeError, and a.b?.c evaluates to undefined.
- This means that (a.b?.c).d will throw a TypeError, because the expression attempts to access a property of the value undefined.
-

But a.b?.c.d (without the parentheses) simply evaluates to undefined and does not throw an error. This is because property access with ?. is "short-circuiting": if the subexpression to the left of ?. evaluates to null or undefined, then the entire expression immediately evaluates to undefined without any further property access attempts.


- Of course, if a.b is an object, and if that object has no property named c, then a.b?.c.d will again throw a TypeError which we can then prevent by using another conditional operator: a.b?.c?.d.
Expand Gist Expand Code Snippet
- Conditional property access is also possible using ?.[] instead of []. For instance, in the expression a?.[b][c], if the value of a is null or undefined, then the entire expression immediately evaluates to undefined, and the subexpressions b and c are never evaluated.
- If either of those expressions have side effects, the side effect will not occur if a is not defined.
Expand Gist Expand Code Snippet
Conditional Invocations of Functions
- Similar to property access, you can also call a function conditionally.
- For instance, consider myFunction(). Here if myFunction was null or undefined, a TypeError would be thrown. But if you did myFunction?.(), this would evaluate to undefined and no exception would be thrown.
- The same short-circuiting rules regarding increment of passed arguments apply as well.
- Consider the following three different cases:
Expand Gist Expand Code Snippet
Some JS Operators
- delete: remove a property
- typeof: determine type of operand
- instanceof: test object class
- in: test whether property exists
- The / operator divides its first operand by its second. If you are used to programming languages that distinguish between integer and floating point numbers, you might expect to get an integer result when you divide one integer by another. In JavaScript, however, all numbers are floating-point, so all division operations have floating-point results: 5/2 evaluates to 2.5, not 2.
- You should almost always be using the strict-equality operators: === and !== .
When are two values equal when using === ?
- Two value are equal when comparing with === as per the following rules:
a) If the two values have different types, they are not equal
b) If both values are null or both values are undefined, they are equal
c) If both values are the boolean value true or both are the boolean value false, they are equal
d) If one or both values is NaN, they are not equal. (This is surprising, but the NaN value is never equal to any other value, including itself! To check whether a value x is NaN, use x !== x, or the global isNaN() function.)
e) If both values are numbers and have the same value, they are equal. If one value is 0 and the other is -0, they are also equal.
f) If both values are strings and contain exactly the same 16-bit values in the same positions, they are equal. Two strings may have the same meaning and the same visual appearance, but still be encoded using different sequences of 16-bit values. JavaScript performs no Unicode normalization, and a pair of strings like this is not considered equal to the === or == operators.
g) If both values refer to the same object, array, or function, they are equal. If they refer to different objects, they are not equal, even if both objects have identical properties.

- Note that the <= (less than or equal) and >= (greater than or equal) operators do not rely on the equality or strict equality operators for determining whether two values are "equal." Instead, the less-than-or-equal operator is simply defined as "not greater than," and the greater-than-or-equal operator is defined as "not less than."
The in operator
- The in operator expects a left-side operand that is a string, symbol, or value that can be converted to a string.
- It expects a right-side operand that is an object.
- It evaluates to true if the left-side value is the name of a property of the right-side object.
Expand Gist Expand Code Snippet
The instanceof operator
- // TODO: In order to understand how the instanceof operator works, you must understand the "prototype chain."
Logical && operator
- The && operator can be understood at three different levels:
a) At the simplest level, when used with boolean operands, && performs the Boolean AND operation on the two values: it returns true if and only if both its first operand and its second operand are true. If one or both of these operands is false, it returns false.
b) The second level at which && can be understood is as a Boolean AND operator for truthy and falsy values. If both operands are truthy, the operator returns a truthy value. Otherwise, one or both operands must be falsy, and the operator returns a falsy value. In JavaScript, any expression or statement that expects a boolean value will work with a truthy or falsy value,

so the fact that && does not always return true or false does not cause practical problems

.
c) Notice that this description says that the operator returns "a truthy value" or "a falsy value" but does not specify what that value is. For that, we need to describe && at the third and final level. This operator starts by evaluating its first operand, the expression on its left. If the value on the left is falsy, the value of the entire expression must also be falsy, so && simply returns the value on the left and does not even evaluate the expression on the right. On the other hand, if the value on the left is truthy, then the overall value of the expression depends on the value on the right hand side. If the value on the right is truthy, then the overall value must be truthy, and if the value on the right is falsy, then the overall value must be falsy. So when the value on the left is truthy, the && operator evaluates and returns the value on the right.
Expand Gist Expand Code Snippet
Logical || operator
- Like the && operator discussed above, || starts by evaluating its first operand, the expression on its left. If the value of this first operand is truthy, it short-circuits and returns that truthy value without ever evaluating the expression on the right. If, on the other hand, the value of the first operand is falsy, then || evaluates its second operand and returns the value of that expression.
Expand Gist Expand Code Snippet
?: and ?? operators
- Ternary operator: ? :
- First-Defined operator: ??
- Writing a ?? b is equivalent to writing (a !== null && a !== undefined) ? a : b.
Expand Gist Expand Code Snippet
The delete operator
- delete is a unary operator that attempts to delete the object property or array element specified as its operand.
Expand Gist Expand Code Snippet

Chapter 5: Statements

Random stuff
- The rule in JS (as in most programming languages) is that by default an else clause is part of the nearest if statement. - Note the difference in behavior of the continue statement in the while and for loops: a while loop returns directly to its condition, but a for loop first evaluates its increment expression and then returns to its condition. Earlier, we considered the behavior of the for loop in terms of an "equivalent" while loop. Because the continue statement behaves differently for these two loops, however, it is not actually possible to perfectly simulate a for loop with a while loop alone.
switch statement
- The general format of a switch statement looks something like this: example on MDN.
- When a switch executes, it computes the value of expression and then looks for a case label whose expression evaluates to the same value (where sameness is determined by the === operator). If it finds one, it starts executing the block of code at the statement labeled by the case. If it does not find a case with a matching value, it looks for a statement labeled default: If there is no default: label, the switch statement skips the block of code altogether.
for/of loop
- This loop works with iterable objects. What constitutes an iterable object will be discussed in Chapter 12. For now, just know that arrays, strings, sets, and maps are iterable.
- Note that arrays are iterated "live" - changes made during the iteration may affect the outcome of the iteration. If we modified the preceding code by adding the line data.push(sum); inside the loop body, then we create an infinite loop because the iteration can never reach the last element of the array.
Expand Gist Expand Code Snippet
- Objects are not by-default iterable. Attempting to use for/of on a regular object throws a TypeError at runtime.
- Object.keys(): If you want to iterate through the properties of an object, you can use the for/in loop, or use the for/of with the Object.keys() method. This works because Object.keys() returns an array of property names for an object, and arrays are iterable with for/of. Note also that this iteration of the keys of an object is not live as the array example above was - changes to the object obj made in the loop body will have no effect on the iteration.
- Object.values(): Similarly, if you are interested only in the values of the object, you can iterate through the object using this.
- Object.entries(): And use this if you are interested in both keys and values of an object's properties. Object.entries() returns an array of arrays, where each inner array represents a key/value pair for one property of the object. We use destructuring assignment in this code example to unpack those inner arrays into two individual variables.
Expand Gist Expand Code Snippet
for/of with strings
- Strings are iterable character-by-character.
- Note that strings are iterated by Unicode codepoint, not by UTF-16 character. The string "IπŸ–€πŸˆ" has a length of 5 because the two emoji characters each require two UTF-16 characters to represent. But if you iterate that string with for/of, the loop body will run 3 times, once for each of the three code points "I", "πŸ–€", and "🐈".
for/of with sets and maps
- The built-in ES6 Set and Map classes are iterable. This is how you would iterate over them
Expand Gist Expand Code Snippet
for/in
- While a for/of loop requires an iterable object after the of, a for/in loop works with any object after the in. The for/in statement loops through the property names of a specified object.
- // Something ...
- The for/in loop does not actually enumerate all properties of an object. It does not enumerate properties whose names are symbols. And of the properties whose names are strings, it only loops over the enumerable properties, as in, the various built-in methods and properties defined by core JS are not enumerable (for eg. toString()). The bottom line here is

prefer to use a for/of loop with Object.keys() instead of a for/in loop. When working with arrays, you almost always want to use for/of instead of for/in.

throw and try/catch/finally
- An exception is a signal that indicates that some sort of exceptional condition or error has occurred. To throw an exception is to signal such an error or exceptional condition. To catch an exception is to handle it using a try/catch/finally statement.
- The throw statement has the following syntax: throw expresion where expression may evaluate to a value of any type. You might decide to throw a number, or a string, or something else.
- When the JS interpreter itself throws an error, it uses the Error class and its subclasses.
- When a exception is thrown, the JS interpreter immediately stops normal program execution and jumps to the nearest exception handler. An exception handler is the catch clause of a try/catch/finally block. If the block of code in which the exception was thrown does not have an associated catch clause, the interpreter checks the next highest enclosing block of code to see if it has an exception handler associated with it. This continues until a handler is found. In this way, exceptions propagate up through the lexical structure of JS methods and up the call stack. If no exception handler is ever found, the exception is treated as an error and is reported to the user.
- The following code illustrates the syntax and purpose of the try/catch/finally statement:
Expand Gist Expand Code Snippet

Chapter 6: Objects

Random stuff
-