You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: objects-classes/ch1.md
+150-5Lines changed: 150 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -178,12 +178,14 @@ The computed property name used to define the property on `anotherObj` will be t
178
178
179
179
Because symbols are globally unique in your program, there's **no** chance of accidental collision where one part of the program might accidentally define a property name the same as another part of the program tried defined/assigned.
180
180
181
+
Symbols are also useful to hook into special default behaviors of objects, and we'll cover that in more detail in "Extending the MOP" in the next chapter.
182
+
181
183
### Concise Properties
182
184
183
185
When defining an object literal, it's common to use a property name that's the same as an existing in-scope identifier that holds the value you want to assign.
184
186
185
187
```js
186
-
coolFact ="The first person convicted of speeding was going 8 mph";
188
+
coolFact ="the first person convicted of speeding was going 8 mph";
187
189
188
190
anotherObj = {
189
191
coolFact: coolFact
@@ -200,7 +202,7 @@ In this situation, where the property name and value expression identifier are i
200
202
coolFact ="the first person convicted of speeding was going 8 mph";
201
203
202
204
anotherObj = {
203
-
coolFact
205
+
coolFact// <-- concise property short-hand
204
206
};
205
207
```
206
208
@@ -533,6 +535,10 @@ myObj.favoriteNumber = 123;
533
535
534
536
If the `favoriteNumber` property doesn't already exist, that statement will create a new property of that name and assign its value. But if it already exists, that statement will re-assign its value.
535
537
538
+
| WARNING: |
539
+
| :--- |
540
+
| An `=` assignment to a property may fail (silently or throwing an exception), or it may not directly assign the value but instead invoke a *setter* function that performs some operation(s). More details on these behaviors in the next chapter. |
541
+
536
542
It's also possible to assign one or more properties at once -- assuming the source properties (name and value pairs) are in another object -- using the `Object.assign(..)` (added in ES6) method:
537
543
538
544
```js
@@ -552,10 +558,149 @@ Object.assign(
552
558
);
553
559
```
554
560
555
-
`Object.assign(..)` takes the first object as target, and the second (and optionally subsequent) objects as source(s). Copying is done in the same manner as described earlier in the "Object Spread" section.
561
+
`Object.assign(..)` takes the first object as target, and the second (and optionally subsequent) object(s) as source(s). Copying is done in the same manner as described earlier in the "Object Spread" section.
562
+
563
+
## Deleting Properties
564
+
565
+
Once a property is defined on an object, the only way to remove it is with the `delete` operator:
566
+
567
+
```js
568
+
anotherObj = {
569
+
counter:123
570
+
};
571
+
572
+
anotherObj.counter; // 123
573
+
574
+
deleteanotherObj.counter;
575
+
576
+
anotherObj.counter; // undefined
577
+
```
578
+
579
+
Contrary to common misconception, the JS `delete` operator does **not** directly do any deallocation/freeing up of memory, through garbage collection (GC). The only thing it does is remove a property from an object. If the value in the property was a reference (to another object/etc), and there are no other surviving references to that value once the property is removed, that value would likely then be eligible for removal in a future sweep of the GC.
580
+
581
+
Calling `delete` on anything other than an object property is a misuse of the `delete` operator, and will either fail silently (in non-strict mode) or throw an exception (in strict mode).
582
+
583
+
Deleting a property from an object is distinct from assigning it a value like `undefined` or `null`. A property assigned `undefined`, either initially or later, is still present on the object, and might still be revealed when enumerating the contents
584
+
585
+
## Determining Container Contents
586
+
587
+
You can determine an object's contents in a variety of ways. To ask an object if it has a specific property:
588
+
589
+
```js
590
+
myObj = {
591
+
favoriteNumber:42,
592
+
coolFact:"the first person convicted of speeding was going 8 mph",
593
+
beardLength:undefined,
594
+
nicknames: [ "getify", "ydkjs" ]
595
+
};
596
+
597
+
"favoriteNumber"in myObj; // true
598
+
599
+
myObj.hasOwnProperty("coolFact"); // true
600
+
myObj.hasOwnProperty("beardLength"); // true
601
+
602
+
myObj.nicknames=undefined;
603
+
myObj.hasOwnProperty("nicknames"); // true
604
+
605
+
deletemyObj.nicknames;
606
+
myObj.hasOwnProperty("nicknames"); // false
607
+
```
608
+
609
+
There *is* an important difference between how the `in` operator and the `hasOwnProperty(..)` method behave. The `in` operator will check not only the target object specified, but if not found there, it will also consult the object's `[[Prototype]]` chain (covered in the next chapter). By contrast, `hasOwnProperty(..)` only consults the target object.
610
+
611
+
If you're paying close attention, you may have noticed that `myObj` appears to have a method property called `hasOwnProperty(..)` on it, even though we didn't define such. That's because `hasOwnProperty(..)` is defined as a built-in on `Object.prototype`, which by default is "inherited by" all normal objects. There is risk inherent to accessing such an "inherited" method, though. Again, more on prototypes in the next chapter.
612
+
613
+
### Better Existence Check
614
+
615
+
ES2022 (almost official at time of writing) has already settled on a new feature, `Object.hasOwn(..)`. It does essentially the same thing as `hasOwnProperty(..)`, but it's invoked as a static helper external to the object value instead of via the object's `[[Prototype]]`, making it safer and more consistent in usage:
616
+
617
+
```js
618
+
// instead of:
619
+
myObj.hasOwnProperty("favoriteNumber")
620
+
621
+
// we should now prefer:
622
+
Object.hasOwn(myObj,"favoriteNumber")
623
+
```
624
+
625
+
Even though (at time of writing) this feature is just now emerging in JS, there are polyfills that make this API available in your programs even when running in a previous JS environment that doesn't yet have the feature defined. For example, a quick stand-in polyfill sketch:
Including a polyfill patch such as that in your program means you can safely start using `Object.hasOwn(..)` for property existence checks no matter whether a JS environment has `Object.hasOwn(..)` built in yet or not.
637
+
638
+
### Listing All Container Contents
639
+
640
+
We already discussed the `Object.entries(..)` API earlier, which tells us what properties an object has (as long as they're enumerable -- more in the next chapter).
641
+
642
+
There's a variety of other mechanisms available, as well. `Object.keys(..)` gives us list of the enumerable property names (aka, keys) in an object -- names only, no values; `Object.values(..)` instead gives us list of all values held in enumerable properties.
643
+
644
+
But what if we wanted to get *all* the keys in an object (enumerable or not)? `Object.getOwnPropertyNames(..)` seems to do what we want, in that it's like `Object.keys(..)` but also returns non-enumerable property names. However, this list **will not** include any Symbol property names, as those are treated as special locations on the object. `Object.getOwnPropertySymbols(..)` returns all of an object's Symbol properties. So if you concatenate both of those lists together, you'd have all the direct (*owned*) contents of an object.
645
+
646
+
Yet as we've implied several times already, and will cover in full detail in the next chapter, an object also can also "inherit" contents from its `[[Prototype]]` chain. These are not considered *owned* contents, so they won't show up in any of these lists.
647
+
648
+
Recall that the `in` operator will potentially traverse the entire chain looking for the existence of a property. Similarly, a `for..in` loop will traverse the chain and list any enumerable (owned or inhertied) properties. But there's no built-in API that will traverse the whole chain and return a list of the combined set of both *owned* and *inherited* contents.
649
+
650
+
## Temporary Containers
651
+
652
+
Using a container to hold multiple values is sometimes just a temporary transport mechanism, such as when you want to pass multiple values to a function via a single argument, or when you want a function to return multiple values:
653
+
654
+
```js
655
+
functionformatValues({ one, two, three }) {
656
+
// the actual object passed in as an
657
+
// argument is not accessible, since
658
+
// we destructured it into three
659
+
// separate variables
660
+
661
+
one =one.toUpperCase();
662
+
two =`--${two}--`;
663
+
three =three.substring(0,5);
664
+
665
+
// this object is only to transport
666
+
// all three values in a single
667
+
// return statement
668
+
return { one, two, three };
669
+
}
670
+
671
+
// destructuring the return value from
672
+
// the function, because that returned
673
+
// object is just a temporary container
674
+
// to transport us multiple values
675
+
const { one, two, three } =
676
+
677
+
// this object argument is a temporary
678
+
// transport for multiple input values
679
+
formatValues({
680
+
one:"Kyle",
681
+
two:"Simpson"
682
+
three:"getify"
683
+
});
684
+
685
+
one; // "KYLE"
686
+
two; // "--Simpson--"
687
+
three; // "getif"
688
+
```
689
+
690
+
The object literal passed into `formatValues(..)` is immediately parameter destructured, so inside the function we only deal with three separate variables (`one`, `two`, and `three`). The object literal `return`ed from the function is also immediately destructured, so again we only deal with three separate variables (`one`, `two`, `three`).
691
+
692
+
This snippet illustrates the idiom/pattern that an object is sometimes just a temporary transport container rather than a meaningful value in and of itself.
693
+
694
+
## Containers Are Collections Of Properties
695
+
696
+
The most common usage of objects is as containers for multiple values. We create and manage property container objects by:
556
697
557
-
## Container Overview
698
+
* defining properties (named locations), either at object creation time or later
699
+
* assigning values, either at object creation time or later
700
+
* accessing values later, using the location names (property names)
But there's a lot more to objects than just static collections of property names and values. In the next chapter, we'll dive under the hood to look at how they actually work.
560
705
561
706
[^structuredClone]: "Structured Clone Algorithm", HTML Specification, https://html.spec.whatwg.org/multipage/structured-data.html#structured-cloning
0 commit comments