Deep Dive into JS Object Property Accessors
Tags: javascript
Published: 2020-06-28 02:27:11

## Revision of Objects

An object is a collection of key-value pairs.

```js
let obj = {
  key1: "value1",
  key2: "value2",
  ...
};
```

**Values** are also called as **properties** or **methods**, depending on the type of value. If the value is a `function`, then it's called **method**, otherwise, it's called **property**.

**Keys** are considered as names of the **property** or **method**. Each **key** point to a single **value**.

```js
let obj = {
  propertyName: "Value",
  methodName: function () {
    console.log("I'm a method");
  },
};
```

**Note:** A method is simply a property which is callable.

More about **Property Names**:

- Any property name which is a valid variable name can be used with or without the quotes. Example: `Name` or `"Name"`

- Any property name which is an invalid variable name can only be used with quotes i.e. as a `string`. Example: `"Full Name"`

- Any property name which is a `number`, can be used with or without quotes, Example: `123`, `"123"`, `1.23`, `"1.23"`

- A property name could also be a **reserved keyword**, Example: `if`, `else`, `function`, `break`, etc. (But, it's suggested to not keep such property names for avoiding confusion)

```js
let obj = {
  Name: "Divyansh",
  "Full Name": "Divyansh Batham",
  123: "One Hundred Twenty Three",
  1.23: "One point Two Three",
  if: "Peru",
  else: "Croatia",
};
```

**Note:** With or without quotes the keys refer to the same property, and whenever possible try to skip the quotes (This is generally how code formatters like Prettier formats your code).

## What are Property Accessors

Property Accessors allow access to the object's property (Reading, Creating, Updating) using the property name or keys.

In Javascript, there are two notations with which we access the properties of an object:

1. Dot Notation `.`
2. Bracket Notation `[]`

If a matching **key**( or **property name** or **method name**) is not found in the object, the property accessors returns `undefined`.

## Dot Notation `.`

```js
objectName.propertyName;
```

The `propertyName` must be a valid **variable name** or a **reserved keyword** for this Notation to work. Keys which have quotes will also not work.

```js
let obj = {
  Name: "Divyansh",
  "Full Name": "Divyansh Batham",
  123: "One Hundred Twenty Three",
  1.23: "One point Two Three",
  if: "Peru",
  else: "Croatia",
};

obj.Name; // "Divyansh"
obj.XYZ; // undefined
obj."Full Name"; // Uncaught SyntaxError: Unexpected string
obj.Full Name; // Uncaught SyntaxError: Unexpected identifier (Name)
obj.123; // Uncaught SyntaxError: Unexpected number
obj."123"; // Uncaught SyntaxError: Unexpected string
obj.1.23; // Uncaught SyntaxError: Unexpected number
obj."1.23"; // Uncaught SyntaxError: Unexpected string
obj.if; // "Peru"
obj.else; // "Croatia"
```

## Bracket Notation `[]`

```js
objectName[expression];
```

An Expression is any valid unit of code that resolves/evaluates to a value. The resolved value is then typecasted into a `string` and that value is considered as the `propertyName`.

**Note:** Any `propertyName` which is a **keyword** cannot be accessed with this, as it will give an `Unexpected Token Error`.

### Simple Expressions

```js
let obj = {
  string: "Marshall Islands",
  123: "Wallis & Futuna",
  true: "Uzbekistan",
};

obj["string"]; // "Marshall Islands"
obj[123]; // "Wallis & Futuna"
obj["123"]; // "Wallis & Futuna"
obj["true"]; // "Uzbekistan"
obj[true]; // "Uzbekistan"
obj[string]; // Uncaught ReferenceError: string is not defined
```

**Note:** You shouldn't use the property name without quotes here, unless it's a `number` or a `boolean`, as it could become an invalid expression. (Explained Below)

### Expressions which are Variables

```js
let obj = {
  foobar: "Bermuda",
};

obj["foobar"]; // "Bermuda"
obj[foobar]; // Uncaught ReferenceError: foobar is not defined
```

In `obj[foobar]`, `foobar` is basically looked as a variable (since there are no quotes to make it a `string`), now since in our code there was no variable named `foobar` we got the error.

If our code had a variable named `foobar`, then the expression (or variable) `foobar` would have been resolved to its value, which then would have been typecasted to get the **property name** or **key**.

```js
let foobar = "Yemen";
let obj = {
  foobar: "Bermuda",
  Yemen: "Mozambique",
};

obj["foobar"]; // "Bermuda"
obj[foobar]; // "Mozambique" Since the value of foobar is "Yemen"
```

### Expressions which are not straight away values

**Remember the Golden Rule**: First evaluate the expression, and then typecast the resolved value into a `string` to get the **property name** or **key**.

**For Example:**

- `true && false` evaluate to `false` (boolean), which is then typecasted into a `string` so we get the property name to be searched is `"false"`.

- `500 - 100` evaluate to `400` (number), which is then typecasted into a `string` so the property name to be searched is `"400"`.

- `"foo" + "bar"` evaluate to `"foobar"` (string), which is a string so no typecast needed and the property name to be searched is `"foobar"`.

```js
let obj = {
  true: "Uzbekistan",
  false: "Belgium",
  foo: "Mongolia",
  bar: "Tanzania",
  foobar: "Norway",
  1000: "Syria",
};

obj[100 > 10]; // "Uzbekistan"
obj[true && false]; // "Belgium"
obj[100 > 10 ? "foo" : "bar"]; // "Mongolia"
obj["foo" + "bar"]; // "Norway"
obj[1500 - 500]; // "Syria"
```

### Some Typecasts to keep in mind

#### 1. Arrow Functions:

```js
(x => x * x).toString(); // "x => x * x"
```

Any leading or trailing spaces are ignored:

```js
(       x => x * x).toString(); // "x => x * x"
(x => x * x       ).toString(); // "x => x * x"
(    x => x * x   ).toString(); // "x => x * x"
```

Any spaces between the start and end of an arrow function is kept.

```js
(x     => x * x).toString(); // "x     => x * x"
(x   =>   x * x).toString(); // "x   =>   x * x"
```

#### 2. Objects:

Doesn't matter what the object is, when typecasted into a string it will always become `"[object Object]"`.

```js
({}.toString()); // "[object Object]"
({ foo: "bar1" }.toString()); // "[object Object]"
({ foo: "bar2" }.toString()); // "[object Object]"
```

### Some Tricky Expressions 

Try to guess the outputs yourself, answers at the bottom.

```js
let obj = {
    function: "Spain",
    "x => x * x": "Portugal",
    " x => x * x ": "Indonesia",
    "x=>x*x": "Taiwan",
};

obj[function]; // ?
obj["function"]; // ?
obj[()=>{}]; // ?
obj[x => x * x]; // ?
obj["x => x * x"]; // ?
obj[ x => x * x ]; // ?
obj[" x => x * x "]; // ?
obj["x=>x*x"]; // ?
obj[x=>x*x]; // ?
```

```js
let obj = {};

obj[{}]; // ?
obj[{}] = "Ukraine";
obj[{ a: "Cuba" }]; // ?
obj[{ a: "Iran" }] = "France";

obj[{ a: "New Zealand" }]; // ?
obj[{ b: "Liberia" }]; // ?
```

### Answers

```js
let obj = {
    function: "Spain",
    "x => x * x": "Portugal",
    " x => x * x ": "Indonesia",
    "x=>x*x": "Taiwan",
};

obj[function]; // Uncaught SyntaxError: Unexpected token ']'
obj["function"]; // "Spain"
obj[()=>{}]; // undefined
obj[x => x * x]; // "Portugal"
obj["x => x * x"]; // "Portugal"
obj[ x => x * x ]; // "Portugal"
obj[" x => x * x "]; // "Indonesia"
obj["x=>x*x"]; // "Taiwan"
obj[x=>x*x]; // "Taiwan"
```

```js
let obj = {};

obj[{}]; // undefined
obj[{}] = "Ukraine";
obj[{ a: "Cuba" }]; // "Ukraine"
obj[{ a: "Iran" }] = "France";

obj[{ a: "New Zealand" }]; // "France"
obj[{ b: "Liberia" }]; // "France"
```