|
7 | 7 |
|
8 | 8 | Now that we're comfortable with the built-in primitive types, we turn our attention to the `object` types in JS.
|
9 | 9 |
|
10 |
| -The "Objects & Classes" title of this series covers objects in-depth already, so make sure you've read that before continuning with this chapter. Here we'll focus our attention on how the `object` value-type behaves and interacts with other values in JS. |
| 10 | +I could write a whole book talking about objects in-depth; in fact, I already did! The "Objects & Classes" title of this series covers objects in-depth already, so make sure you've read that before continuning with this chapter. |
11 | 11 |
|
12 |
| -## Nature Of Objects |
| 12 | +Rather than repeat that book's content, here we'll focus our attention on how the `object` value-type behaves and interacts with other values in JS. |
13 | 13 |
|
14 |
| -The `object` value-type comprises several sub-types with specialized behaviors. These include: |
| 14 | +## Types of Objects |
15 | 15 |
|
16 |
| -* general objects |
| 16 | +The `object` value-type comprises several sub-types, each with specialized behaviors, including: |
| 17 | + |
| 18 | +* plain objects |
17 | 19 | * fundamental objects (boxed primitives)
|
| 20 | +* built-in objects |
18 | 21 | * arrays
|
19 | 22 | * regular expressions
|
20 | 23 | * functions (aka, "callable objects")
|
21 | 24 |
|
22 |
| -But one shared characteristic is that all objects are capable of acting as collections of values. |
| 25 | +Beyond the specialized behaviors, one shared characteristic is that all objects can act as collections (of properties) holding values (including functions/methods). |
23 | 26 |
|
24 |
| -// TODO |
| 27 | +## Plain Objects |
| 28 | + |
| 29 | +The general object value-type is sometimes referred to as *plain ol' javascript objects* (POJOs). |
| 30 | + |
| 31 | +Plain objects have a literal form: |
| 32 | + |
| 33 | +```js |
| 34 | +address = { |
| 35 | + street: "12345 Market St", |
| 36 | + city: "San Francisco", |
| 37 | + state: "CA", |
| 38 | + zip: "94114" |
| 39 | +}; |
| 40 | +``` |
| 41 | + |
| 42 | +This plain object (POJO), as defined with the `{ .. }` curly braces, is a collection of named properties (`street`, `city`, `state`, and `zip`). Properties can hold any values, primitives or other objects (including arrays, functions, etc). |
| 43 | + |
| 44 | +The same object could also have been defined imperatively using the `new Object()` constructor: |
| 45 | + |
| 46 | +```js |
| 47 | +address = new Object(); |
| 48 | +address.street = "12345 Market St"; |
| 49 | +address.city = "San Francisco"; |
| 50 | +address.state = "CA"; |
| 51 | +address.zip = "94114"; |
| 52 | +``` |
| 53 | + |
| 54 | +Plain objects are by default `[[Prototype]]` linked to `Object.prototype`, giving them delegated access to several general object methods, such as: |
| 55 | + |
| 56 | +* `toString()` / `toLocaleString()` |
| 57 | +* `valueOf()` |
| 58 | +* `isPrototypeOf(..)` |
| 59 | +* `hasOwnProperty(..)` (recently deprecated -- alternative: static `Object.hasOwn(..)` utility) |
| 60 | +* `propertyIsEnumerable(..)` |
| 61 | +* `__proto__` (getter function) |
| 62 | + |
| 63 | +```js |
| 64 | +address.isPrototypeOf(Object.prototype); // true |
| 65 | +address.isPrototypeOf({}); // false |
| 66 | +``` |
25 | 67 |
|
26 | 68 | ## Fundamental Objects
|
27 | 69 |
|
28 |
| -In Chapter 2, I briefly mentioned *auto-boxing*, which we'll now revisit. [^AutoBoxing] |
| 70 | +JS defines several *fundamental* object types, which are instances of various built-in constructors, including: |
| 71 | + |
| 72 | +* `new String()` |
| 73 | +* `new Number()` |
| 74 | +* `new Boolean()` |
| 75 | + |
| 76 | +Note that these constructors must be used with the `new` keyword to construct instances of the fundamental objects. Otherwise, these functions actually perform type coercion (see Chapter 4). |
| 77 | + |
| 78 | +These fundamental object constructors create object value-types instead of a primitives: |
| 79 | + |
| 80 | +```js |
| 81 | +myName = "Kyle"; |
| 82 | +typeof myName; // "string" |
| 83 | + |
| 84 | +myNickname = new String("getify"); |
| 85 | +typeof myNickname; // "object" |
| 86 | +``` |
| 87 | + |
| 88 | +In other words, an instance of a fundamental object constructor can actually be seen as a wrapper around the corresponding underlying primitive value. |
| 89 | + |
| 90 | +| WARNING: | |
| 91 | +| :--- | |
| 92 | +| It's nearly universally regarded as *bad practice* to ever directly instantiate these fundamental objects. The primitive counterparts are generally more predictable, more performant, and offer *auto-boxing* (see "Automatic Objects" section below) whenever the underlying object-wrapper form is needed for property/method access. | |
| 93 | + |
| 94 | +The `Symbol(..)` and `BigInt(..)` functions are referred to in the specification as "constructors", though they're not used with the `new` keyword, and the values they produce in a JS program are indeed primitives. |
| 95 | + |
| 96 | +How, there are internal *fundamental objects* for these two types, used for prototype delegation and *auto-boxing*. |
| 97 | + |
| 98 | +By contrast, for `null` and `undefined` primitive values, there aren't `Null()` or `Undefined()` "constructors", nor corresponding fundamental objects or prototypes. |
| 99 | + |
| 100 | +### Prototypes |
| 101 | + |
| 102 | +Instances of the fundamental object constructors are `[[Prototype]]` linked to their constructors' `prototype` objects: |
| 103 | + |
| 104 | +* `String.prototype`: defines `length` property, as well as string-specific methods, like `toUpperCase()`, etc. |
| 105 | + |
| 106 | +* `Number.prototype`: defines number-specific methods, like `toPrecision(..)`, `toFixed(..)`, etc. |
| 107 | + |
| 108 | +* `Boolean.prototype`: defines default `toString()` and `valueOf()` methods. |
| 109 | + |
| 110 | +* `Symbol.prototype`: defines `description` (getter), as well as default `toString()` and `valueOf()` methods. |
| 111 | + |
| 112 | +* `BigInt.prototype`: defines default `toString()`, `toLocaleString()`, and `valueOf()` methods. |
| 113 | + |
| 114 | +Any direct instance of the built-in constructors have `[[Prototype]]` delegated access to its respective `prototype` properties/methods. Moreover, corresponding primitive values also have such delegated access, by way of *auto-boxing*. |
| 115 | + |
| 116 | +### Automatic Objects |
| 117 | + |
| 118 | +I've mentioned *auto-boxing* several times (including Chapters 1 and 2, and a few times so far in this chapter). It's finally time for us to explain that concept. |
| 119 | + |
| 120 | +Accessing a property or method on a value requires that the value be an object. As we've already seen in Chapter 1, primitives *are not* objects, so JS needs to then temporarily convert/wrap such a primitive to its fundamental object counterpart[^AutoBoxing] to perform that access. |
| 121 | + |
| 122 | +For example: |
| 123 | + |
| 124 | +```js |
| 125 | +myName = "Kyle"; |
| 126 | + |
| 127 | +myName.length; // 4 |
| 128 | + |
| 129 | +myName.toUpperCase(); // "KYLE" |
| 130 | +``` |
| 131 | + |
| 132 | +Accessing the `length` property or the `toUpperCase()` method, is only allowed on a primitive string value because JS *auto-boxes* the primitive `string` into a wrapper fundamental object, an instance of `new String(..)`. Otherwise, all such accesses would have to fail, since primitives do not have any properties. |
| 133 | + |
| 134 | +More importantly, when the primitive value is *auto-boxed* to its fundamental object counterpart, those internally created objects have access to predefined properties/methods (like `length` and `toUpperCase()`) via a `[[Prototype]]` link to their respective fundamental object's prototype. |
| 135 | + |
| 136 | +So an *auto-boxed* `string` is an instance of `new String()`, and is thus linked to `String.prototype`. Further, the same is true of `number` (wrapped as an instance of `new Number()`) and `boolean` (wrapped as an instance of `new Boolean()`). |
| 137 | + |
| 138 | +Even though the `Symbol(..)` and `BigInt(..)` "constructors" (used without `new`produce primitive values, these primitive values can also be *auto-boxed* to their internal fundamental object wrapper forms, for the purposes of delegated access to properties/methods. |
| 139 | + |
| 140 | +| NOTE: | |
| 141 | +| :--- | |
| 142 | +| See the "Objects & Classes" book of this series for more on `[[Prototype]]` linkages and delegated/inherited access to the fundamental object constructors' prototype objects. | |
| 143 | + |
| 144 | +Since `null` and `undefined` have no corresponding fundamental objects, there is no *auto-boxing* of these values. |
| 145 | + |
| 146 | +A subjective question to consider: is *auto-boxing* a form of coercion? I say it is, though some disagree. Internally, a primitive is converted to an object, meaning a change in value-type has occurred. Yes, it's temporary, but plenty of coercions are temporary. Moreover, the conversion is rather *implicit* (implied by the property/method access, but only happens internally). We'll revisit the nature of coercion in Chapter 4. |
| 147 | + |
| 148 | +## Other Built-in Objects |
| 149 | + |
| 150 | +In addition to fundamental object constructors, JS defines a number of other built-in constructors that create further specialized object sub-types: |
| 151 | + |
| 152 | +* `new Date(..)` |
| 153 | +* `new Error(..)` |
| 154 | +* `new Map(..)`, `new Set(..)`, `new WeakMap(..)`, `new WeakSet(..)` -- keyed collections |
| 155 | +* `new Int8Array(..)`, `new Uint32Array(..)`, etc -- indexed, typed-array collections |
| 156 | +* `new ArrayBuffer(..)`, `new SharedArrayBuffer(..)`, etc -- structured data collections |
| 157 | + |
| 158 | +## Arrays |
| 159 | + |
| 160 | +Arrays are objects that are specialized to behave as numerically indexed collections of values, as opposed to holding values at named properties like plain objects do. |
| 161 | + |
| 162 | +Arrays have a literal form: |
| 163 | + |
| 164 | +```js |
| 165 | +favoriteNumbers = [ 3, 12, 42 ]; |
| 166 | + |
| 167 | +favoriteNumbers[2]; // 42 |
| 168 | +``` |
| 169 | + |
| 170 | +The same array could also have been defined imperatively using the `new Array()` constructor: |
| 171 | + |
| 172 | +```js |
| 173 | +favoriteNumbers = new Array(); |
| 174 | +favoriteNumbers[0] = 3; |
| 175 | +favoriteNumbers[1] = 12; |
| 176 | +favoriteNumbers[2] = 42; |
| 177 | +``` |
| 178 | + |
| 179 | +Arrays are `[[Prototype]]` linked to `Array.prototype`, giving them delegated access to a variety of array-oriented methods, such as `map(..)`, `includes(..)`, etc: |
| 180 | + |
| 181 | +```js |
| 182 | +favoriteNumbers.map(v => v * 2); |
| 183 | +// [ 6, 24, 84 ] |
| 184 | + |
| 185 | +favoriteNumbers.includes(42); // true |
| 186 | +``` |
| 187 | + |
| 188 | +Some of the methods defined on `Array.prototype` -- for example, `push(..)`, `pop(..)`, `sort(..)`, etc -- behave by modifying the array value in place. Other methods -- for example, `concat(..)`, `map(..)`, `slice(..)` -- behave by creating a new array to return, leaving the original array intact. A third category of array functions -- for example, `indexOf(..)`, `includes(..)`, etc -- merely computes and returns a (non-array) result. |
| 189 | + |
| 190 | +## Regular Expressions |
29 | 191 |
|
30 | 192 | // TODO
|
31 | 193 |
|
| 194 | +## Functions |
| 195 | + |
| 196 | +// TODO |
| 197 | + |
| 198 | +## Proposed: Records/Tuples |
| 199 | + |
| 200 | +At the time of this writing, a (stage-2) proposal[^RecordsTuplesProposal] exists to add a new set of features to JS, which correspond closely to plain objects and arrays, but with some notable differences. |
| 201 | + |
| 202 | +Records are similar to plain objects, but are immutable (sealed, read-only), and (unlike objects) are treated as primitive values, for the purposes of value assignment and equality comparison. The syntax difference is a `#` before the `{ }` delimiter. Records can only contain primitive values (including records and tuples). |
| 203 | + |
| 204 | +Tuples have exactly the same relationship, but to arrays, including the `#` before the `[ ]` delimiters. |
| 205 | + |
| 206 | +It's important to note that while these look and seem like objects/arrays, they are indeed primitive (non-object) values. |
| 207 | + |
| 208 | +[^FundamentalObjects]: "20 Fundamental Objects", EcamScript 2022 Language Specification; https://262.ecma-international.org/13.0/#sec-fundamental-objects ; Accessed August 2022 |
| 209 | + |
32 | 210 | [^AutoBoxing]: "6.2.4.6 PutValue(V,W)", Step 5.a, ECMAScript 2022 Language Specification; https://262.ecma-international.org/13.0/#sec-putvalue ; Accessed August 2022
|
| 211 | + |
| 212 | +[^RecordsTuplesProposal]: "JavaScript Records & Tuples Proposal"; Robin Ricard, Rick Button, Nicolò Ribaudo; |
| 213 | +https://github.com/tc39/proposal-record-tuple ; Accessed August 2022 |
0 commit comments