JavaScript オブジェクト 未定義判定（途中の添字も含む）

####追記 オプショナルチェインニング演算子
コメントにて、もっとシンプルな方法を教えていただきました。ありがとうございます。

```
const test = {
    apple: {
        2019: { A: 15000, B: 16000, C: 12000, D: 21000 },
        2021: { A: 15000, C: 16000 },
    },
};

console.log(test?.["apple"]?.[2020]?.["A"]);
// undefined

console.log(test?.["apple"]?.[2021]?.["C"]);
//  16000
```

この例では、```?.``` を用いています。オプショナルチェインニング演算子、という名前らしいです。なかなか格好いい名前です。

ドキュメントによると、
>オプショナルチェイニング演算子 ?. は、接続されたオブジェクトチェーンの深くに位置するプロパティの値を、チェーン内の各参照が正しいかどうかを明示的に確認せずに読み込むことを可能にします。 ?. 演算子の機能は . チェーン演算子と似ていますが、参照が nullish (null または undefined) の場合にエラーとなるのではなく、式が短絡され undefined が返されるところが異なります。 関数呼び出しで使用すると、与えられた関数が存在しない場合、 undefined を返します。

>これは、参照が失われた可能性のある連結されたプロパティにアクセスする時、結果的に短く単純な式になります。また、必要なプロパティの存在が保証されていない場合にオブジェクトのコンテンツを探索するのにも役立ちます。

参考：[Optional chaining (?.) - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Optional_chaining)

ということで、未定義のオブジェクトにアクセスしようとしたとき、エラーではなく undefined を返してくれる演算子です。
これを使えば、別で関数を用意する必要もなく、一行でシンプルに書けるため、これを使うのが一番良さそうですね。
ただ、この演算子は IE には対応していないようなので、もし対応が必要であれば、下に書いた方法を参考にしていただくと良いと思います。

教えていただき、ありがとうございました。


####はじめに

私はよく、JavaScriptで多次元のオブジェクトを使います。今回はオブジェクトの未定義の判定についてです。途中で未定義になる場合の対処について書いてみます。

単純に、オブジェクトが未定義、または null であるかは以下のように判定できます。

```js
const test = {"id":1,"name":test};
if(test["age"] == null){
    console.log("未定義です。");
}
// 未定義です。
```

しかし、以下のような場合はエラーとなります。

```js
const test = {
    apple: {
        2019: { A: 15000, B: 16000, C: 12000, D: 21000 },
        2021: { A: 15000, C: 16000 },
    },
};

if(test["apple"][2020]["A"] == null){
    console.log("未定義です。");
}
// Uncaught TypeError: test.apple[2020] is undefined
```

この場合、```test["apple"][2020]``` が未定義であるため、```test["apple"][2020]["A"]``` のnull判定をする前にエラーが出てしまうというわけです。

こんなオブジェクト作るなよ、と思われるかもしれませんが、とりあえず、このようなオブジェクトで途中でもちゃんと判定できるようにする方法を2つ考えてみました。

####1つ目 論理演算子

```js
const test = {
    apple: {
        2019: { A: 15000, B: 16000, C: 12000, D: 21000 },
        2021: { A: 15000, C: 16000 },
    },
};

if (
   ((test && 
     test["apple"] && 
     test["apple"][2020] && 
     test["apple"][2020]["A"]) ||
     null) 
        == null
) {
        console.log("未定義です。");
}
// 未定義です。
```

この例では、論理演算子を使っています。
A && B とすると、A が true であれば B の値を、A が false であれば A の値を採用します。
A || B とすると、A が false であれば B の値を、A が true であれば A の値を採用します。

undefined は false 扱いですので、


```test ```　
```test["apple"]```　
```test["apple"][2020]``` 　
```test["apple"][2020]["A"]``` 


のうちどれかが未定義であれば false となり、false || null で右の値が採用されて、null となります。
逆に、全てが定義されていれば、true || null で左の値が採用されるというわけです。

しかしこの方法では、一々記述が長くなる、undefined 以外に false 判定される値（０とか）が入っているとうまく動かないといった問題点があります。ということで、関数で判定できるように書いてみました。

####2つ目 関数

```js
const test = {
    apple: {
        2019: { A: 15000, B: 16000, C: 12000, D: 21000 },
        2021: { A: 15000, C: 16000 },
    },
};

function returnLastKeysValueIfDefined(object, keys) {
    if (object == null) {
        return false;
    }
    for (let i = 0; i < keys.length; i++) {
        if (!(object instanceof Object) || object[keys[i]] == null) {
            return false;
        }
        object = object[keys[i]];
    }
    return object;
}

if(!(returnLastKeysValueIfDefined(test,["apple",2020,"A"]))){
    console.log("未定義です。");
}
// 未定義です。

console.log(returnLastKeysValueIfDefined(test,["apple",2021,"A"]));
// 15000
```

この関数では、引数1つ目に判定したいオブジェクトを入れます。引数2つ目には、判定したいキーを、順番に配列にして入れます。

見て分かる通り、この関数では、単純に最初から見ていき、null であれば false を返しています。すべて値があれば、最後の値を返しています。

普通は null の判定だけでよいのですが、

```js
const str = "こんにちは。";
console.log(str[1]);
// ん
```

このように、string 型に数字の添字を使うと１文字ずつアクセスできます。今回の場合はこれでは都合が悪いため、

```js
if (!(object instanceof Object) || object[keys[i]] == null) {
        return false;
}
```

このようにオブジェクトであるかの判定を入れて、回避しています。

最後に、正直JavaScript（というかプログラミング自体）があまり良くわかっていない状態で書いていますので、致命的なミスや、こっちのほうが良いなどあると思います。その時は教えていただけるとありがたいです。
読んでいただき、ありがとうございました。