# obj.prop(path)

{% hint style="success" %}
如果 JavaScript 引擎不支援 [](https://lochiwei.gitbook.io/web/js/val/obj/prop/access/optional-chaining "mention")，就可以用下面的程式。
{% endhint %}

{% tabs %}
{% tab title="💾 程式" %}
{% hint style="warning" %}
由於使用 [mixin](https://lochiwei.gitbook.io/web/js/val/obj/extend/mixin "mention") 的方式，所以當我們使用 <mark style="color:blue;">`user.prop('info.age')`</mark> 的方式來讀取物件屬性時，<mark style="color:blue;">`user`</mark> 本身已經<mark style="color:yellow;">**確定是個物件**</mark>(不會是 null 或 undefined)，因此我們在 <mark style="color:blue;">`prop(path)`</mark> 這個 method 內部並沒有去檢查 <mark style="color:purple;">`this`</mark> 是不是 null 或 undefined。
{% endhint %}

* replit ⟩ [obj.prop(path)](https://replit.com/@pegasusroe/JS-objproppath#index.js)

```javascript
// ⭐ mixin for objects
const ObjectTools = {
    
    // obj.prop(path)
    prop(path) {
        
        let value = this;
        let components = path.split('.');
        
        while (components.length) {
            
            // pull first component
            let component = components.shift();
            // check if last character === '?'
            const isValueOptional = component.slice(-1) === '?';

            if (isValueOptional) {
                // remove "?"
                component = component.slice(0, -1);
                // get nested property
                value = value[component];
                // return early if value is nullish
                if (value === null || value === undefined) return undefined;
            } else {
                // if not optional, get property directly
                value = value[component];
            }
            
        }
        
        return value;
    }
};

// for node.js module
module.exports = objectTools;
```

💈範例：

```javascript
"use strict";
const { log } = console;

// ⭐ mixin for objects
const ObjectTools = require('./ObjectTools.js');

// ---------- main ------------

// test object
const user = { 
    name: 'Joy',
    info: { 
        address: { 
            street: '5th Street',
            zip: 333,
        } 
    } 
};

// ⭐ apply mixin to `obj`
Object.assign(user, ObjectTools);

// ---------- log ------------
;
[
    user.prop("info.address"),           // { street: '5th Street', zip: 333 }
    user.prop("info.address.street"),    // '5th Street'
    
    user.prop("info.address.street.bar"),// undefined
    user.prop("info.age"),               // undefined
    user.prop("info.age?.details"),      // undefined
    user.prop("bar?.age?.details"),      // undefined
    
    // user.prop("bar.age"),             // ⛔ TypeError: 
    //                ^^^  <----  Cannot read property 'age' of undefined

    user === global.user,                // false: `const` doesn't create property of `global`
    
].forEach(x => log(x));

// user.prop("info.address.street")
// ---------------------------------
// initial states:
//
//   • value = user
//   • components = [ 'info', 'address', 'street' ] 
//
// while loop:
// --------------------------------------------------------------------
// length  shift()    components             value           
// --------------------------------------------------------------------
//   3     'info'    ['address', 'street']    user.info
//   2     'address' ['street']               user.info.address
//   1     'street'  [ ]                      user.info.address.street
//   0    <---- exit while loop              ╰── return value ───╯
// --------------------------------------------------------------------
```

{% endtab %}

{% tab title="📘 手冊" %}

* [eval()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval) ⟩ [accessing member properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#accessing_member_properties) ⭐️
* [String.prototype.slice()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice)
* [Global object](https://developer.mozilla.org/en-US/docs/Glossary/Global_object)\
  unlike [`var`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var), [`let`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let) and [`const`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const) <mark style="color:red;">**do not**</mark> create **properties** of the <mark style="color:yellow;">**global object**</mark>.
  {% endtab %}

{% tab title="👥 相關" %}

* use the concept of [mixin](https://lochiwei.gitbook.io/web/js/val/obj/extend/mixin "mention").
  {% endtab %}

{% tab title="🗣 討論" %}

* [How to get the last character of a string?](https://stackoverflow.com/a/3884711/5409815)  Ans:&#x20;

  ```javascript
  str.slice(-1);
  ```
* [JavaScript check if variable exists (is defined/initialized)](https://stackoverflow.com/questions/5113374/javascript-check-if-variable-exists-is-defined-initialized)
* [JavaScript: How to check if variable exists by variable name?](https://stackoverflow.com/questions/9928914/javascript-how-to-check-if-variable-exists-by-variable-name)
* [String to variable name without eval](https://stackoverflow.com/questions/56653910/string-to-variable-name-without-eval)
  {% endtab %}
  {% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lochiwei.gitbook.io/web/js/val/obj/prop/access/optional-chaining/obj.prop-path.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
