* comma-separated list of colon-separated `name:value` pairs, enclosed within `{}`.
* property *name*: JS identifier or string
* property *value*: JS expression
```js
let empty = {}; // An object with no properties
let point = { x: 0, y: 0 }; // Two numeric properties
let p2 = { x: point.x, y: point.y+1 }; // More complex values
let book = {
"main title": "JavaScript", // These property names include spaces,
"sub-title": "The Definitive Guide", // and hyphens, so use string literals.
for: "all audiences", // for is reserved, but no quotes.
author: { // The value of this property is
firstname: "David", // itself an object.
surname: "Flanagan"
}
};
```
When object literal works:
* Object literal creates & initializes a new & distinct obj every time it's evaluated.
* In loop body, a new obj can be created repeatedly.
### 6.2.2 Creating Objects with new
* **`new` operator**
* creates & initialize a new object
* syntax: `new` followed by function invocation as **constructor**
```js
let o = new Object(); // Create an empty object: same as {}.
let a = new Array(); // Create an empty array: same as [].
let d = new Date(); // Create a Date object representing the current time
let r = new Map(); // Create a Map object for key/value mapping
```
### 6.2.3 Prototypes
Almost all JS obj has a prototype associate with it:
* All objs created using **object literal** (shown in 6.2.1) are associated with the same prototype obj, referred by `Object.prototype`
* objs created using `new` (invoking constructor) use *value of constructor function's `prototype` property* as prototype
*`new Object()` inherits from `Object.prototype`
*`new Array()` inherits from `Array.prototype`
* Only few objects have `prototype` property, they are used to define `prototypes` for all other objs.
### 6.2.4 Object.create()
3 Methods below demonstrated ability to create a new obj with an arbitrary prototype:
* Create new obj w/ defined prototype using **`Object.create()`**
```js
let o1 = Object.create({x: 1, y: 2}); // o1 inherits properties x and y.
o1.x + o1.y // => 3
```
* Create new obj w/o prototype by parsing `null`
* Created obj inherit no property or method (e.g. `toString()`)
```js
let o2 = Object.create(null); // o2 inherits no props or methods.
```
* Create ordinary new empty obj using **`Object.prototype`** (like obj returned by `{}` or `Object()`)
```js
let o3 = Object.create(Object.prototype); // o3 is like {} or new Object().
```
#### Use created object to guard unintended modification
* Q: How to guard against unintended modification of an obj by a function (from other library)?
* A: Instead of passing the obj directly to the function, pass an obj that inherit from it. So writing property do not affect original value. (like passing a read-only)
```js
let o = { x: "don't change this value" };
library.function(Object.create(o)); // Guard against accidental modifications
* using dot (`.`): RHS of dot should be simple identifier (not string) of property
* using square bracket (`[]`): value within `[]` should be an expression that evalutes to a string (or sth can convert to string) that contains property name
```js
let author = book.author; // Get the "author" property of the book.
let name = author.surname; // Get the "surname" property of the author.
let title = book["main title"]; // Get the "main title" property of the book.
```
Create/Set a property:
* Query property, and place it on LHS
```js
book.edition = 7; // Create an "edition" property of book.
book["main title"] = "ECMAScript"; // Change the "main title" property.
```
### 6.3.1 Objects As Associative Arrays
```js
object.property // C like structure access
object["property"] // associative array
```
JS objects are **Associative Arrays** (e.g. hash or map or dictionary)
* In strong typed language (e.g. C/C++), obj's property are defined. While, JS program can **create any number of properties in any object in runtime**
*`.` operator requires name of the property as identifier, which may be unknown in code.
JS obj have a set of "own properties", and they also inherit properties from prototype chain.
Read properties:
* If a property cannot be found in a JS obj, it will search one by one (bottom to top, from child to parent) through prototype chain
```js
let o = {}; // o inherits object methods from Object.prototype
o.x = 1; // and it now has an own property x.
let p = Object.create(o); // p inherits properties from o and Object.prototype
p.y = 2; // and has an own property y.
let q = Object.create(p); // q inherits properties from p, o, and...
q.z = 3; // ...Object.prototype and has an own property z.
let f = q.toString(); // toString is inherited from Object.prototype
q.x + q.y // => 3; x and y are inherited from o and p
```
Write (assign) properties:
* check prototype chain only to verify whether read-only.
* If inherited property `x` is read-only, assignment is not allowed
* If assignment is allowed, the property is created/set within the current object, and do not modify prototype chain
* Only exception: if `o` inherits property `x`, and that property is an accessor property with a setter method, then the setter method is called rather than creating a new property `x` within `o`.
```js
let unitcircle = { r: 1 }; // An object to inherit from
let c = Object.create(unitcircle); // c inherits the property r
c.x = 1; c.y = 1; // c defines two properties of its own
c.r = 2; // c overrides its inherited property
unitcircle.r // => 1: the prototype is not affected
let len = book.subtitle.length; // !TypeError: undefined doesn't have length
```
Method to guard against this problem type:
* Method 1: verbose and explicit
```js
let surname = undefined;
if (book) {
if (book.author) {
surname = book.author.surname;
}
}
```
* Method 2: A concise and idiomatic alternative to get surname or null or undefined
* Check Chap4.10.1 for short-circuiting behavior of && operator
```js
surname = book && book.author && book.author.surname;
```
* Method 3: Rewrite method 2 using `?.`
```js
let surname = book?.author?.surname;
```
Lists of tips:
* Attempting to set property on `null` or `undefined` causes a `TypeError`
* Attempting to set property may fail due to
* Some properties are read-only
* Some objects don't allow adding new properties
* Error from property assignment:
* In strict mode (Chap 5.6.3), a TypeError is thown whenever an attempt to set a property fails.
* Outsie strict mode, silent when fail
3 circumstances when failed to set a property `p` of obj `o`:
1.`o` has an own property `p` that is read-only
2.`o` has an inherited property `p` that is read-only: No way to overwrite this property
3.`o` does not have an own property `p`; `o` does not inherit a property `p` with a setter method, and `o`’s extensible attribute (see §14.2) is `false`.
1. Since `p` does not already exist in `o`, and if there is no setter method to call, then `p` must be added to `o`.
2. But if `o` is not extensible, then no new properties can be defined on it.
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.
* 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()
All JS objects (excpet explicitly created w/o prototype) inherits properties from `Object.prototype`. Hence they inherited some **primarily methods**, which are universally available.