Finished chapter 6.7: Extending Objects

master
Jason Zhu 2021-01-12 18:37:10 +11:00
parent 35212b2b19
commit e381a49e37
1 changed files with 92 additions and 0 deletions

View File

@ -344,10 +344,102 @@ delete o.x; // Delete the property x
## 6.6 Enumerating Properties
We want to iterate through or obtain a list of all properties of an object using:
* `for/in` loop.
* Get an array of property names for an object and then loop through that array with `for/of` loop.
**Method 1: Use `for/in` loop to enumerate properties**
1. runs the body of the loop once for each enumerable property (own or inherited) of the specified obj.
2. assigning the name of the property to the loop variable.
Note:
* Inherited built-in methods are not enumerable (e.g. "toString")
* Properties that added by code are enumerable by default.
```js
let o = {x: 1, y: 2, z: 3}; // Three enumerable own properties
o.propertyIsEnumerable("toString") // => false: not enumerable
for(let p in o) { // Loop through the properties
console.log(p); // Prints x, y, and z, but not toString
}
```
Trick: Stop enumerating inherited properties with `for/in`, add an explicit check inside loop body
```js
for(let p in o) {
if (!o.hasOwnProperty(p)) continue; // Skip inherited properties
}
for(let p in o) {
if (typeof o[p] === "function") continue; // Skip all methods
}
```
**Method 2: Get an array of property names for an object and then loop through that array with `for/of` loop.**
Four ways to get an array of property names:
* `Object.keys()`: returns an array of the names of the enumerable own properties of an object, excluding non-enumerable properties, inherited properties, properties whose name is Symbol
* `Object.getOwnPropertyNames()`: returns same as `Ojbect.keys()` + non-enum own properties (as long as name are strings)
* `Object.getOwnPropertySymbols()`: returns own properties whose names are Symboles (no matter they are enumerable or not)
* `Reflect.ownKeys()`: returns all own property names, both enum or non-enum, and both string and Symbol.
### 6.6.1 Property Enumeration Order
Order Summary:
1. String properties whose names are non-negative integers, numeric order from smallest to largest. i.e. arrays are enumerated in order
2. All remaining properties with string names. In order they were added to object.
3. Properties whose names are Symbol in order they were added to obj.
## 6.7 Extending Objects
Common practice in pure JS to copy properties from one obj to another:
```js
let target = {x: 1}, source = {y: 2, z: 3};
for(let key of Object.keys(source)) {
target[key] = source[key];
}
target // => {x: 1, y: 2, z: 3}
```
Various JS frameworks have developed utility function `extend()` to cover this operation, and it's standarized in ES6 as `Object.assign()`
**`Object.assign()`**:
* Syntax: `Object.assign(target_obj, src_obj_1, src_obj_2)`
* Operations: Copy enumerable own properties from 2nd and subsequent args (i.e. **source object**) to 1st arg (i.e. **target object**)
* target obj is modified & returned
* source obj is not changed
* Copy order follows order of arg, so 1st source object property will overwrite target obj property, while 2nd source obj property will overwrite 1st obj property
* How copy works: using ordinary property get/set operations.
* if source has getter method, and target has setter, these 2 methods will be invoked. But themselves won't be copied.
Trick to use `.assign()`:
* Given obj `o`, and source `defaults`. Directly assign will overwrite `o`'s original properties if there are same property names
```js
Object.assign(o, defaults); // overwrites everything in o with defaults
```
* Correct way of safely copy properties while keeping target's original property values: create a new object, copy the defaults into it, and then override those defaults with the properties in `o`
```js
o = Object.assign({}, defaults, o);
```
A function can be created to solve this problem via copies properties only if they are missing:
```js
// Like Object.assign() but doesn't override existing properties
// (and also doesn't handle Symbol properties)
function merge(target, ...sources) {
for(let source of sources) {
for(let key of Object.keys(source)) {
if (!(key in target)) { // This is different than Object.assign()
target[key] = source[key];
}
}
}
return target;
}
Object.assign({x: 1}, {x: 2, y: 2}, {y: 3, z: 4}) // => {x: 2, y: 3, z: 4}
merge({x: 1}, {x: 2, y: 2}, {y: 3, z: 4}) // => {x: 1, y: 2, z: 4}
```
## 6.8 Serializing Objects
## 6.9 Object Methods