> For the complete documentation index, see [llms.txt](https://lochiwei.gitbook.io/web/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lochiwei.gitbook.io/web/js/val/obj/prop/access/optional-chaining.md).

# optional chaining (?., ?.\[])

\`obj ?. prop\` syntax. 🚧 under construction -> SyntaxError: Unexpected token

* [JS](/web/js.md) ⟩ [statement](/web/js/grammar/statement.md) ⟩ [expression](/web/js/grammar/statement/expr.md) ⟩ [operator](/web/js/grammar/op.md) ⟩ [left-hand side](/web/js/grammar/statement/expr/lhs.md) ⟩&#x20;
* [JS](/web/js.md) ⟩ [value](/web/js/val.md) ⟩ [object](/web/js/val/obj.md) ⟩ [property](/web/js/val/obj/prop.md) ⟩ [access](/web/js/val/obj/prop/access.md) ⟩ optional chaining (?.)

{% hint style="success" %} <mark style="color:yellow;">**(**</mark>[operator](/web/js/grammar/op.md)<mark style="color:yellow;">**) (**</mark>[⭐️ ES2020](/web/js/feature/es2020.md)<mark style="color:yellow;">**)**</mark> (:star2: [**chaining rules**](/web/js/val/obj/prop/access/chaining-rules.md) <mark style="color:yellow;">**|**</mark> [**table of operators** ](/web/js/grammar/op/table-of-operators.md))

<mark style="color:yellow;">**`obj`**</mark><mark style="color:purple;">**`?.`**</mark><mark style="color:yellow;">**`prop`**</mark> returns [**undefined**](/web/js/val/prim/undefined.md) <mark style="color:red;">**if**</mark> <mark style="color:yellow;">**`obj`**</mark> <mark style="color:yellow;">**is**</mark> [<mark style="color:red;">**nullish**</mark>](/web/js/val/prim/nullish.md).

```javascript
obj  ?. prop        // dot notation
obj  ?. [ expr ]    // bracket notation
func ?. ( args )    // conditional invocation
```

:u6307: <mark style="color:yellow;">**synonyms**</mark>**：**"<mark style="color:purple;">**conditional property access**</mark>", "<mark style="color:purple;">**optional chaining**</mark>"
{% endhint %}

{% tabs %}
{% tab title="🗺️" %} <img src="https://2527454625-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MfvEFZnSBhKT6fJmus0%2Fuploads%2FL7fbZTDQS5owq1y9IJlx%2Foptional.chaining.svg?alt=media&amp;token=b9d8905f-6bb7-4e3e-b8e1-1214f6569787" alt="" class="gitbook-drawing">

* codepen：[optional chaining](https://codepen.io/lochiwei/pen/jOKERRa?editors=0012)

```javascript
// ⭐️ 1. prop: invalid identifier
// ------------------------------
undeclared ?. 1 = 'bad';  // ⛔ SyntaxError: Unexpected token
//            ^

// ⭐️ 2. obj: "undeclared"
// -------------------------------
undeclared ?. prop;       // ⛔ ReferenceError: 'undeclared' is not defined

// ⭐️ 3. obj: "nullish"
// -------------------------------
null ?. prop              // ❗ undefined

// ⭐️ 4. obj: not "nullish"
// -------------------------------
(18) ?. prop;            // ❗ undefined (no such `prop`)
(18) ?. toString(16);    // ❓ "12" (property value, could be any value)

// test object
let person = {
    
    name   : 'Tom',
    age    : 28,
    address: { street: 'Fifth Ave.', no: '23' },
    hobbies: ['TV', 'Game'],
    
    eat(food) { return 'poop' }
};
    
'------------ person ------------',

// ❗ "root" object (person) is NEVER protected

person ?. name,                 // "Tom"
//        ╰🛡️╯                
person ?. hobby,                // undefined
//        ╰🛡️─╯
person ?. address ?. street,    // "Fifth Ave."
//        ╰─🛡️──╯    ╰─🛡️─╯ 
person . address ?. district,   // undefined (✅ address: non-nullish)
//       ╰─❗──╯    ╰──🛡️──╯
person . hobby ?. [1],          // undefined (✅ hobby: nullish, protected)
//       ╰❗─╯   ╰🛡️╯ 
person . hobbies ?. [2],        // undefined (✅ hobbies: non-nullish)
//       ╰─❗──╯   ╰🛡️╯ 
person ?. eat ?. ('egg'),       // "poop"    (✅ eat: non-nullish)
//       ╰🛡️╯    ╰─🛡️──╯ 
person ?. make ?. (something),  // undefined (✅ make: direct prop, protected, short-circuiting)
//       ╰🛡️─╯    ╰───🛡️────╯


// jane
let jane = { 
    home     : null,
    boyfriend: { name: 'Joe' }
};

'------------ jane ------------',

// ❗ "root" object (jane) is NEVER protected

jane .  home,            // null (prop value)
//      ╰❗╯
jane ?. home,            // null (jane: non-nullish, = jane.home)
//      ╰🛡️╯ 

// 🛡️: "?." protect "nullish" obj and "direct" prop
// ❗: no protection, could be undeclared / nullish / error
// -----------------------------------------------------------
jane . home ?. c  ,          // undefined (✅ home: null, protected)
//     ╰❗╯   ╰🛡️╯ 
jane . home ?. ['hello'],    // undefined (✅ home: null, protected)
//     ╰❗╯   ╰───🛡️───╯ 

jane . home ?.  c .  d ,     // undefined (✅ home: null, short-circuiting)
//     ╰❗╯   ╰🛡️╯ ╰❗╯

// ╭─ UD ─╮         // UD: UnDefined
// (a.b?.c).d,      // ❌ DON'T DO THIS! (⛔ TypeError❗)
//       ^

jane . boyfriend ?. name,    // 'Joe' ( ✅ boyfriend: non-nullish)
//     ╰──❗───╯    ╰🛡️╯   
jane . boyfriend ?. money,   // undefine ( ✅ money: direct prop, protected)
//     ╰──❗───╯    ╰🛡️─╯   

jane . boyfriend ?. money . more,  // ⛔ TypeError (accessing `undefined.more`)
//     ╰──❗───╯    ╰🛡️─╯  ╰❗─╯      // (❌ more: not protected)

jane . boyfriend ?. money ?. more, // undefined ✅ (more: direct prop, protected)
//     ╰──❗───╯    ╰🛡️─╯   ╰🛡️─╯ 
```

{% endtab %}

{% tab title="🧨" %}
{% hint style="danger" %}
⭐️ the variable <mark style="color:yellow;">**before**</mark>**&#x20;**<mark style="color:purple;">**`?.`**</mark> <mark style="color:red;">**must**</mark>**&#x20;be&#x20;**<mark style="color:yellow;">**declared**</mark>❗️

```javascript
// ╭───❗️───╮  <----- ⭐️ "root" object is NEVER protected❗️
   undeclared?.address;
// ⛔ ReferenceError: `undeclared` is not defined
```

{% endhint %}

{% hint style="danger" %}
[jane.boyfriend?.money.more](/web/js/val/obj/prop/access/chaining-rules/jane.boyfriend-.money.more.md)&#x20;

<mark style="color:purple;">**`?.`**</mark> only protects nullish obj and direct prop:exclamation:

```javascript
// 🛡️ "?." only protects "nullish obj" and "direct prop"
// ❗ "."  provides NO protection.
// ❗ "root" object is NEVER protected.
jane . boyfriend ?. name          // ✅ boyfriend: non-nullish, ok.
//     ╰──❗───╯    ╰🛡️╯        
jane . boyfriend ?. money . more  // ✅ money: direct prop, protected.
//     ╰──❗───╯   ╰─🛡️─╯  ╰❗╯  // ❗ more: not protected, error may occur. 
```

{% endhint %}

{% hint style="danger" %}
⭐️ use <mark style="color:blue;">**`?.`**</mark> for <mark style="color:green;">**reading**</mark> / <mark style="color:green;">**deleting**</mark>, but <mark style="color:red;">**not**</mark> <mark style="color:red;">**writing**</mark>❗️

```javascript
user?.name           // ✅ read if exists
delete user?.name    // ✅ delete if exists.

user?.name = "John"  // ⛔ SyntaxError
// Invalid left-hand side in assignment
// this is literally `undefine = "John"`❗️ 
```

{% endhint %}

{% hint style="danger" %}
the right [bracket notation \[\]](/web/js/val/obj/prop/access/bracket-notation.md) for <mark style="color:purple;">**optional chaining**</mark>

```javascript
obj ?. [ expr ]     // ✅ there's a tiny "dot" ❗️ 
obj ?  [ expr ]     // ❌ wrong syntax
```

{% endhint %}

{% hint style="danger" %} <mark style="color:yellow;">**while**</mark> <mark style="color:purple;">**optional chaining**</mark>, <mark style="color:red;">**don't**</mark>**&#x20;**<mark style="color:yellow;">**use**</mark> <mark style="color:blue;">`(...)`</mark> <mark style="color:yellow;">**in the middle**</mark> to <mark style="color:red;">**prevent**</mark> "<mark style="color:yellow;">**short-circuiting**</mark>" from happening:exclamation:

```javascript
let a = { b: null };

a.b?.c.d,        // ✅ this is OK (⭐ "short-circuiting" works.)

// ╭─ UD ─╮      // UD: undefined
   (a.b?.c).d,   // ❌ DON'T DO THIS! (⛔ TypeError❗)
//          ^    
```

{% endhint %}
{% endtab %}

{% tab title="⭐️" %}
{% hint style="success" %}
:star: <mark style="color:purple;">**`?.`**</mark>**&#x20;**<mark style="color:yellow;">**protects**</mark> [<mark style="color:red;">**nullish**</mark>](/web/js/val/prim/nullish.md) <mark style="color:blue;">**`obj`**</mark>**&#x20;**<mark style="color:yellow;">**and**</mark>**&#x20;**<mark style="color:red;">**direct**</mark> <mark style="color:blue;">**`prop`**</mark><mark style="color:yellow;">**, so if**</mark> <mark style="color:blue;">**`obj`**</mark>：[<mark style="color:yellow;">**declared**</mark>](/web/js/grammar/token/id/undeclared.md), <mark style="color:blue;">**`prop`**</mark>：[<mark style="color:yellow;">**identifier**</mark>](/web/js/grammar/token/id.md), then it's totally <mark style="color:green;">**safe**</mark> to call <mark style="color:blue;">**`obj ?. prop`**</mark>.
{% endhint %}

{% hint style="warning" %}
:star: 「<mark style="color:orange;">**不確定存不存在**</mark>」的物件或屬性，才需要用 <mark style="color:purple;">**`?.`**</mark>
{% endhint %}

{% hint style="warning" %}
:star: <mark style="color:yellow;">**use**</mark> <mark style="color:purple;">**?.**</mark> <mark style="color:yellow;">**instead**</mark> to <mark style="color:green;">**guard**</mark>**&#x20;**<mark style="color:yellow;">**againt**</mark> [<mark style="color:blue;">**`obj.prop`**</mark>](/web/js/val/obj/prop/access/dot-notation-..md), <mark style="color:yellow;">**should**</mark> <mark style="color:blue;">**`obj`**</mark> <mark style="color:yellow;">**be**</mark> [<mark style="color:red;">**nullish**</mark>](/web/js/val/prim/nullish.md):exclamation:
{% endhint %}

{% hint style="success" %}
:star: <mark style="color:yellow;">**use**</mark> <mark style="color:purple;">**?.**</mark>**&#x20;**<mark style="color:yellow;">**followed by**</mark> [<mark style="color:blue;">**`??`**</mark>](/web/js/grammar/op/logical/nullish-coalescing.md) to <mark style="color:yellow;">**provide**</mark> a <mark style="color:orange;">**default value**</mark>.

```javascript
const city = user ?. city ?? "Unknown city"
//           ╰── O.C. ──╯    ╰─ default ──╯
```

{% endhint %}

{% hint style="info" %}
:information\_source: <mark style="color:blue;">**replit.com**</mark>  (<mark style="color:green;">**v16.13.2**</mark> > <mark style="color:yellow;">**Node 14**</mark>) <mark style="color:yellow;">**supports**</mark> <mark style="color:purple;">**?.**</mark>
{% endhint %}

{% hint style="info" %}
:information\_source:[Google Apps Script](/web/appendix/gas.md) <mark style="color:yellow;">**supports**</mark> <mark style="color:purple;">**optional chaining**</mark>.
{% endhint %}

{% hint style="success" %}
⭐️ 注意：

當瀏覽器或 JavaScript engine 沒有此功能時，可用 [obj.prop(path)](/web/js/val/obj/prop/access/optional-chaining/obj.prop-path.md) 或下面的方式解決：
{% endhint %}

* replit ⟩ [optional chaining](https://replit.com/@pegasusroe/JS-object-optional-chaining#index.js) ( support：❌ )

```javascript
let user = {};
user.address;           // undefined

// ⛔ TypeError: 
// ----------------------------------------------
user.address.street;    //    Cannot read property 'street' of undefined
//           ^^^^^^
```

💊 解藥：

```javascript
// ⭐️ 1. using (?:) operator
// -------------------------
// • access `user.address.street`
user.address ? user.address.street : undefined,    // undefined

// • access `user.address.street.name`
user.address
    ? (user.address.street ? user.address.street.name : null)
    : null;                                    // null

// ⭐️ 2. using (&&) operator
// -------------------------
// • access `user.address.street.name`
user.address 
    && user.address.street 
    && user.address.street.name;               // undefined
```

{% endtab %}

{% tab title="🔴" %}

* :star2:[chaining rules](/web/js/val/obj/prop/access/chaining-rules.md)
* :beginner:[short-circuiting](/web/js/grammar/op/term/short-circuiting.md)
  {% endtab %}

{% tab title="👥" %}

* :point\_right: [**`obj . prop`**](/web/js/val/obj/prop/access/dot-notation-..md) <mark style="color:yellow;">**/**</mark> [**`obj [ prop ]`**](/web/js/val/obj/prop/access/bracket-notation.md)  (<mark style="color:red;">**non-optional**</mark>**&#x20;**<mark style="color:yellow;">**chaining**</mark>).
* :point\_right: [**`condition ? a : b`**](/web/js/grammar/op/ternary/conditional-operator.md)
* :white\_check\_mark: <mark style="color:yellow;">**use**</mark> **`obj ?. prop`** [**`??`**](/web/js/grammar/op/logical/nullish-coalescing.md) **`default`** to <mark style="color:yellow;">**provide**</mark> <mark style="color:orange;">**default value**</mark>.
* :adhesive\_bandage: <mark style="color:yellow;">**use**</mark> [obj.prop(path)](/web/js/val/obj/prop/access/optional-chaining/obj.prop-path.md) <mark style="color:yellow;">**instead**</mark> <mark style="color:red;">**if**</mark> <mark style="color:purple;">**`?.`**</mark> <mark style="color:red;">**not**</mark>**&#x20;**<mark style="color:yellow;">**supported**</mark>. &#x20;
* :information\_source: [**`[...]`**](/web/js/grammar/token/punctuator/brackets.md) <mark style="color:yellow;">**/**</mark> [**`?.`**](/web/js/grammar/token/punctuator/question-dot-..md) <mark style="color:yellow;">**punctuators**</mark> are used in this operator.
  {% endtab %}

{% tab title="💈" %}

* replit：[optional chaining (use cases)](https://replit.com/@pegasusroe/optional-chaining-use-cases#index.js)

```javascript
function square1(x, print) {
    if (print) { print(x) }    // before optional chaining
    return x * x;
}

function square2(x, print) {
    print ?. (x);              // ⭐ optional chaining
    return x * x;
}
```

{% endtab %}

{% tab title="📗" %}

* [x] JS.info ⟩ [Optional chaining '?.'](https://javascript.info/optional-chaining)
* [ ] freeCodeCamp ⟩ [10 New JavaScript Features in ES2020 That You Should Know](https://www.freecodecamp.org/news/javascript-new-features-es2020/)
* [x] [JavaScript: The Definitive Guide](/web/master/ref/javascript-the-definitive-guide.md) ⟩&#x20;
  * [x] 4.4 Property Access Expressions
    {% endtab %}

{% tab title="📘" %}

* [Optional chaining (?.)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining)
* [Expressions and operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators) ⟩ [Property accessors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors)
* Can I use [optional chaining](https://caniuse.com/mdn-javascript_operators_optional_chaining)?
  {% endtab %}

{% tab title="🗣" %}

* [How to use optional chaining in Node.js 12](https://stackoverflow.com/a/59574160/5409815) (:star: supported from <mark style="color:yellow;">**Node 14.0.0**</mark>:exclamation:)
* reddit ⟩ [JS Optional Chaining Polyfill?](https://www.reddit.com/r/webdev/comments/frbct5/js_optional_chaining_polyfill/)
* [Google Apps Script - optional chaining throwing ParseError](https://stackoverflow.com/questions/64346245/google-apps-script-optional-chaining-throwing-parseerror)
  {% endtab %}

{% tab title="🚧" %}

* [ ] SyntaxError: Unexpected token
  {% endtab %}
  {% endtabs %}
