|
1 | 1 | # You Don't Know JS Yet: Types & Grammar - 2nd Edition
|
2 |
| -# Chapter 2: Value Behavior |
| 2 | +# Chapter 2: Value Behaviors |
| 3 | + |
| 4 | +| NOTE: | |
| 5 | +| :--- | |
| 6 | +| Work in progress | |
3 | 7 |
|
4 | 8 | So far, we've explored seven built-in primitive value types in JS: `null`, `undefined`, `boolean`, `string`, `number`, `bigint`, and `symbol`.
|
5 | 9 |
|
6 | 10 | Chapter 1 was quite a lot to take in, much more involved than I bet most readers expected. If you're still catching your breath after reading all that, don't worry about taking a bit of a break before continuing on here!
|
7 | 11 |
|
8 | 12 | Once you're clear headed and ready to move on, let's dig into certain behaviors implied by value types for all their respective values. We'll take a careful and closer look at all of these various behaviors.
|
9 | 13 |
|
10 |
| -## Value Immutability |
| 14 | +## Primitive Immutability |
| 15 | + |
| 16 | +All primitive values are immutable, meaning nothing in a JS program can reach into the contents of the value and modify it in any way. |
11 | 17 |
|
12 |
| -All primitive values are immutable, meaning nothing in a JS program can reach into the inside of the value and modify it in any way. |
| 18 | +```js |
| 19 | +myAge = 42; |
| 20 | + |
| 21 | +// later: |
| 22 | + |
| 23 | +myAge = 43; |
| 24 | +``` |
13 | 25 |
|
14 |
| -New values are created through operations, but these do not modify the original value. |
| 26 | +The `myAge = 43` statement doesn't change the value. It reassigns a different value `43` to `myAge`, completely replacing the previous value of `42`. |
| 27 | + |
| 28 | +New values are also created through various operations, but again these do not modify the original value: |
15 | 29 |
|
16 | 30 | ```js
|
17 | 31 | 42 + 1; // 43
|
18 | 32 |
|
19 | 33 | "Hello" + "!"; // "Hello!"
|
20 | 34 | ```
|
21 | 35 |
|
22 |
| -The values `43` and `"Hello!"` are new, distinct values from the `42` and `"Hello"` values, respectively. |
| 36 | +The values `43` and `"Hello!"` are new, distinct values from the previous `42` and `"Hello"` values, respectively. |
23 | 37 |
|
24 |
| -// TODO |
| 38 | +Even a string value, which looks like merely an array of characters -- and array contents are typically mutable -- is immutable: |
| 39 | + |
| 40 | +```js |
| 41 | +greeting = "Hello."; |
| 42 | + |
| 43 | +greeting[5] = "!"; |
25 | 44 |
|
26 |
| -## Assignments Are Value Copies |
| 45 | +console.log(greeting); // Hello. |
| 46 | +``` |
27 | 47 |
|
28 |
| -Any assignment of a value from one variable/container to another is a *value-copy*. |
| 48 | +| WARNING: | |
| 49 | +| :--- | |
| 50 | +| In non-strict mode, assigning to a read-only property (like `greeting[5] = ..`) silently fails. In strict-mode, the disallowed assignment will throw an exception. | |
| 51 | + |
| 52 | +The nature of primitive values being immutable is not affected *in any way* by how the variable or object property holding the value is declared. For example, whether `const`, `let`, or `var` are used to declare the `greeting` variable above, the string value it holds is immutable. |
| 53 | + |
| 54 | +`const` doesn't create immutable values, it declares variables that cannot be reassigned (aka, immutable assignments) -- see the "Scope & Closures" title of this series for more information. |
| 55 | + |
| 56 | +A property on an object may be marked as read-only -- with the `writable: false` descriptor attribute, as discussed in the "Objects & Classes" title of this series. But that still has no affect on the nature of the value, only on preventing the reassignment of the property. |
| 57 | + |
| 58 | +### Primitives With Properties? |
| 59 | + |
| 60 | +Additionally, properties *cannot* be added to any primitive values: |
| 61 | + |
| 62 | +```js |
| 63 | +greeting = "Hello."; |
| 64 | + |
| 65 | +greeting.isRendered = true; |
| 66 | + |
| 67 | +greeting.isRendered; // undefined |
| 68 | +``` |
| 69 | + |
| 70 | +This snippet looks like it's adding a property `isRendered` to the value in `greeting`, but this assignment silently fails (even in strict-mode). |
| 71 | + |
| 72 | +Property access is not allowed in any way on nullish primitive values `null` and `undefined`. But properties *can* be accessed on all other primitive values -- yes, that sounds counter-intuitive. |
| 73 | + |
| 74 | +For example, all string values have a read-only `length` property: |
| 75 | + |
| 76 | +```js |
| 77 | +greeting = "Hello."; |
| 78 | + |
| 79 | +greeting.length; // 6 |
| 80 | +``` |
| 81 | + |
| 82 | +`length` can not be set, but it can be accesses, and it exposes the number of code-units stored in the value (see "JS Character Encodings" in Chapter 1), which often means the number of characters in the string. |
| 83 | + |
| 84 | +| NOTE: | |
| 85 | +| :--- | |
| 86 | +| Sort of. For most standard characters, that's true; one character is one code-point, which is one code-unit. However, as explained in Chapter 1, extended Unicode characters above code-point `65535` will be stored as two code-units (surrogate halves). Thus, for each such character, `length` will include `2` in its count, even though the character visually prints as one symbol. | |
| 87 | + |
| 88 | +Non-nullish primitive values also have a couple of standard built-in methods that can be accessed: |
| 89 | + |
| 90 | +```js |
| 91 | +greeting = "Hello."; |
| 92 | + |
| 93 | +greeting.toString(); // "Hello." <-- redundant |
| 94 | +greeting.valueOf(); // "Hello." |
| 95 | +``` |
| 96 | + |
| 97 | +Additionally, most of the primitive value-types define their own methods with specific behaviors inherent to that type. We'll cover these later in this chapter. |
| 98 | + |
| 99 | +| NOTE: | |
| 100 | +| :--- | |
| 101 | +| Technically, these sorts of property/method accesses on primitive values are facilitated by an implicit coercive behavior called *auto-boxing*, which we'll cover in the next chapter. | |
| 102 | + |
| 103 | +## Primitive Assignments |
| 104 | + |
| 105 | +Any assignment of a primitive value from one variable/container to another is a *value-copy*: |
29 | 106 |
|
30 | 107 | ```js
|
31 | 108 | myAge = 42;
|
32 | 109 |
|
33 | 110 | yourAge = myAge; // assigned by value-copy
|
| 111 | + |
| 112 | +myAge; // 42 |
| 113 | +yourAge; // 42 |
| 114 | +``` |
| 115 | + |
| 116 | +Here, the `myAge` and `yourAge` variables each have their own copy of the number value `42`. |
| 117 | + |
| 118 | +| NOTE: | |
| 119 | +| :--- | |
| 120 | +| Inside the JS engine, it *may* be the case that only one `42` value exists in memory, and the engine points both `myAge` and `yourAge` variables at the shared value. Since primitive values are immutable, there's no danger in a JS engine doing so. But what's important to us as JS developers is, in our programs, `myAge` and `yourAge` act as if they have their own copy of that value, rather than sharing it. | |
| 121 | + |
| 122 | +If we later reassign `myAge` to `43` (when I have a birthday), it doesn't affect the `42` that's still assigned to `yourAge`: |
| 123 | + |
| 124 | +```js |
| 125 | +myAge++; // sort of like: myAge = myAge + 1 |
| 126 | + |
| 127 | +myAge; // 43 |
| 128 | +yourAge; // 42 <-- unchanged |
34 | 129 | ```
|
35 | 130 |
|
36 |
| -Here, the `myAge` and `yourAge` variables each have their own copy of the number value `42`. That means if we later re-assign `myAge` to `43` when I have a birthday, it doesn't affect the `42` that's still assigned to `yourAge`. |
| 131 | +## String Behaviors |
| 132 | + |
| 133 | +String values have a number of specific behaviors that every JS developer should be aware of. |
| 134 | + |
| 135 | +As previously mentioned, string values have a `length` property that automatically exposes the number of characters (actually, code units). This property can only be accessed; attempts to set it are silently ignored. |
| 136 | + |
| 137 | +### String Character Access |
| 138 | + |
| 139 | +Though strings are not actually arrays, JS allows `[ .. ]` array-style access of its character at a numeric (`0`-based) index: |
| 140 | + |
| 141 | +```js |
| 142 | +greeting = "Hello!"; |
| 143 | + |
| 144 | +greeting[4]; // "o" |
| 145 | +``` |
| 146 | + |
| 147 | +If the value/expression between the `[ .. ]` doesn't resolve to a number, the value will be implicitly coerced to its whole/integer numeric representation (if possible). |
| 148 | + |
| 149 | +```js |
| 150 | +greeting["4"]; // "o" |
| 151 | +``` |
| 152 | + |
| 153 | +If the value/expression resolves to a number outside the integer range of `0` - `length - 1` (or `NaN`), or if it's not a `number` value-type, the access will instead be treated as a property access with the string equivalent property name. If the property access thus fails, the result is `undefined`. |
| 154 | + |
| 155 | +| NOTE: | |
| 156 | +| :--- | |
| 157 | +| We'll cover coercion in-depth later in the book. | |
| 158 | + |
| 159 | +### String Concatenation |
| 160 | + |
| 161 | +Two or more string values can be concatenated (combined) into a new string value, using the `+` operator: |
| 162 | + |
| 163 | +```js |
| 164 | +greeting = "Hello, " + "Kyle!"; |
| 165 | + |
| 166 | +greeting; // Hello, Kyle! |
| 167 | +``` |
| 168 | + |
| 169 | +The `+` operator will act as a string concatenation if either of the two operands (values on left or right sides of the operator) are already a string. |
| 170 | + |
| 171 | +If one operand is a string and the other is not, the one that's not a string will be coerced to its string representation for the purposes of the concatenation. |
| 172 | + |
| 173 | +### String Methods |
| 174 | + |
| 175 | +Strings provide a whole slew of additional string-specific methods (as properties): |
| 176 | + |
| 177 | +* `charAt(..)`: produces a new string value at the numeric index, similar to `[ .. ]`; unlike `[ .. ]`, the result is always a string, either the character at position `0` (if a valid number outside the indices range), or the empty string `""` (if missing/invalid index) |
| 178 | + |
| 179 | +* `at(..)` is similar to `charAt(..)`, but negative indices count backwards from the end of the string |
| 180 | + |
| 181 | +* `charCodeAt(..)`: returns the numeric code-unit (see "JS Character Encodings" in Chapter 1) at the specified index |
| 182 | + |
| 183 | +* `codePointAt(..)`: returns the whole code-point starting at the specified index; if a surrogate pair is found there, the whole character (code-point) s returned |
| 184 | + |
| 185 | +* `substr(..)` / `substring(..)` / `slice(..)`: produces a new string value that represents a range of characters from the original string; these differ in how the range's start/end indices are specified or determined |
| 186 | + |
| 187 | +* `toUpperCase()`: produces a new string value that's all uppercase characters |
| 188 | + |
| 189 | +* `toLowerCase()`: produces a new string value that's all lowercase characters |
| 190 | + |
| 191 | +* `concat(..)`: produces a new string value that's the concatenation of the original string and all of the string value arguments passed in |
| 192 | + |
| 193 | +* `indexOf(..)`: searches for a string value argument in the original string, optionally starting from the position specified in the second argument; returns the `0`-based index position if found, or `-1` if not found |
| 194 | + |
| 195 | +* `lastIndexOf(..)`: like `indexOf(..)` but, from the end of the string (right in LTR locales, left in RTL locales) |
| 196 | + |
| 197 | +* `includes(..)`: similar to `indexOf(..)` but returns a boolean result |
| 198 | + |
| 199 | +* `search(..)`: similar to `indexOf(..)` but with a regular-expression matching as specified |
| 200 | + |
| 201 | +* `trimStart()` / `trimEnd()` / `trim()`: produces a new string value with whitespace trimmed from the start of the string (left in LTR locales, right in RTL locales), or the end of the string (right in LTR locales, left in RTL locales), or both |
| 202 | + |
| 203 | +* `repeat(..)`: produces a new string with the string argument value repeated the specified number of times |
| 204 | + |
| 205 | +* `split(..)`: produces an array of string values as split at the specified string or regular-expression boundaries |
| 206 | + |
| 207 | +* `padStart(..)` / `padEnd(..)`: produces a new string value with padding (default " " whitespace, but can be overriden) applied to either the start (left in LTR locales, right in RTL locales) or the end (right in LTR locales), left in RTL locales), so that the final string result is at least of a specified length |
| 208 | + |
| 209 | +* `startsWith(..)` / `endsWith(..)`: checks either the start (left in LTR locales, right in RTL locales) or the end (right in LTR locales) of the original string for the string value argument; returns a boolean result |
| 210 | + |
| 211 | +* `match(..)` / `matchAll(..)`: returns an array-like regular-expression matching result against the original string |
| 212 | + |
| 213 | +* `replace(..)`: returns a new string with a replacement from the original string, of one or more matching occurrences of the specified regular-expression match |
| 214 | + |
| 215 | +* `big()`, `blink()`, `bold()`, `fixed()`, `fontcolor()`, `fontsize()`, `italics()`, `link()`, `small()`, `strike()`, `sub()`, and `sup()`: historically, these were useful in generating HTML string snippets; they're now deprecated and should be avoided |
| 216 | + |
| 217 | +### Static String Helpers |
| 218 | + |
| 219 | +The following string utility functions are proviced directly on the `String` object, rather than as methods on individual string values: |
| 220 | + |
| 221 | +* `String.fromCharCode(..)` / `String.fromCodePoint(..)`: produce a string from one or more arguments representing the code-units (`fromCharCode(..)`) or whole code-points (`fromCodePoint(..)`) |
| 222 | + |
| 223 | +* `String.raw(..)`: a default template-tag function that allows interpolation on a template literal but prevents character escape sequences from being parsed, so they remain in their *raw* individual input characters from the literal |
| 224 | + |
| 225 | +## Number Behaviors |
37 | 226 |
|
38 | 227 | // TODO
|
0 commit comments