オブジェクトの初期化中にthisで他のプロパティを使うとエラーになる


現在javascriptとHTMLを使ってある機能を作ってるんですけど、制作過程でオブジェクトの定義に関するエラーがあり、抜け出せません。これを解決する方法を教えて下さい。

**エラー内容**

[code]

    test.html:23 Uncaught TypeError: Cannot read properties of undefined (reading 'options')
        at test.html:23:26
    
[/code]

**ソースコード:**

[code]

    <body>
        
        <p id="display"></p>
            <select id="numbers">
                <option value="1">10</option>
                <option value="2">20</option>
                <option value="3">30</option>
                <button id="output">出力</button>
        </select>
            
        <script>
        
        var element = {
            select: document.getElementById('numbers'),
            index: this.select.options.selectedIndex,
            selected: this.select.options[this.index],
            display: document.getElementById('display'),
            button: document.getElementById('output'),
            output: this.button.addEventLister('click', () => {
               element.display.innerText = element.selected.value
            }),
        }
        
        </script>
    
    </body>
[/code]


  
  
主に `this` を使っている部分に問題があります。

  * [関数(`function`)の外にある `this` は グローバルオブジェクト](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/this#%E3%82%B0%E3%83%AD%E3%83%BC%E3%83%90%E3%83%AB%E3%82%B3%E3%83%B3%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88)であり、意図したオブジェクトを指していません
  * そもそも `this` を使っている部分の値は、ユーザーの選択によって初期化時から変わっている可能性があるので、呼ばれるたびに求める必要があります
  * よって、`this` を用いている部分を[関数 (メソッド)](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Working_with_Objects#defining_methods)にすると解決します

以下に参考になりそうなコード、リンクを挙げていますが、基本的には書籍などで体系的に理解することをお勧めします。

### `this` が参照するもの

`this` が参照するものは文脈により様々です。

  * [JavaScript Primer 「関数とthis」](https://jsprimer.net/basic/function-this/)
  * [MDN 「this」](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/this)

しかし、様々とはいっても質問者が期待した機能はありません。

質問のコードでは、オブジェクト初期化子（リテラル）の中で `this`
を用いることで、初期化（生成）しているオブジェクト自身を指すことを期待していますが、JavaScriptにそのような機能はありません。`this`
にそのような機能が無いだけでなく、他にもそのような事を実現する構文は無いと思います。

「`this` で、初期化中のオブジェクトを参照できる」といった表現がされることがありますが、それは主に `new`
で呼び出される関数(コンストラクタ)の中で使用する場合です。

では、質問のコードの `this` が何を指しているかというと、グローバルオブジェクト(ブラウザの場合は `window`
と同じ)です。下のコードで確認して下さい。

[code]

    var a = "グローバル変数 a の値"
    
    let obj = {
        a: "obj のプロパティ a の値",
        what_is_this: this.a,
    }
    
    // ここで、obj.what_is_this の値はグローバル変数 a と同じです。
    // つまり、"グローバル変数 a の値" となっています。
    // "obj のプロパティ a の値" とはなりません。
    console.log("obj.what_is_this の値は: ", obj.what_is_this)
    
    // 上は、ここでの this がグローバルオブジェクトを指すことを示します。
    // もちろん、オブジェクト初期化子の外であっても同じです。
    // よって、以下の値も同じです。
    console.log("this.a の値は:           ", this.a)
[/code]

### メソッドにする例

質問のコードの形をできるだけ残したもの:

[code]

    var element = {
        select: document.getElementById('numbers'),
        getIndex: function() {
            return this.select.options.selectedIndex
        },
        getSelected: function() {
            return this.select.options[this.getIndex()]
        },
        display: document.getElementById('display'),
        button: document.getElementById('output'),
        init: function() {
            this.button.addEventListener('click', () => {
                this.display.innerText = this.getSelected().value
            })
        },
    }
    element.init()
    
[/code]

より慣用的なJavaScript
(コンストラクター関数や[プロトタイプ](https://developer.mozilla.org/ja/docs/Learn/JavaScript/Objects/Object_prototypes)を用いたオブジェクト指向):

[code]

    function App() {
        this.select = document.getElementById('numbers'),
        this.display = document.getElementById('display'),
        this.button = document.getElementById('output'),
        this.button.addEventListener('click', () => {
            this.display.innerText = this.getSelected().value
        })
    }
    App.prototype.getIndex = function() {
        return this.select.options.selectedIndex
    }
    App.prototype.getSelected = function() {
        return this.select.options[this.getIndex()]
    }
    
    var app = new App()
    
[/code]

より[最近の構文](https://developer.mozilla.org/ja/docs/Learn/JavaScript/Objects/Classes_in_JavaScript#ecmascript_2015_classes)を使用したもの:

[code]

    class App {
        select = document.getElementById('numbers')
        display = document.getElementById('display')
        button = document.getElementById('output')
    
        constructor() {
            this.button.addEventListener('click', () => {
                this.display.innerText = this.getSelected().value
            })
        }
        getIndex() {
            return this.select.options.selectedIndex
        }
        getSelected() {
            return this.select.options[this.getIndex()]
        }
    }
    
    let app = new App()
    
[/code]

