JavaScriptの参照を"完全に理解"できるかもしれないクイズ

# 配列をコピーすると毎回バグる人へ
みなさん、JavaScriptの参照は理解していますか？

- 参照、実体
- 破壊メソッド、非破壊メソッド
- シャローコピー、ディープコピー

これを見て、「よく見るけど何のことかよく分かってない」となった人、あるいは、「配列をコピーすると、なんかよく分かんないけど毎回バグる」という人。

そんな人は、このクイズに取り組んでみてください。

ただドキュメントを読むよりも、理解度がグンと上がります。
この記事から出るときには、「**参照、[完全に理解した](https://jp.quora.com/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC%E3%81%8C%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E7%8B%AC%E7%89%B9%E3%81%AA%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC%E7%94%A8%E8%AA%9E)！**」となっているはずです。


# クイズ本編

**問. 次のコードにある、問1から問7までのログの出力結果を答えてください。**

もちろん、コンソールを使わずに、コードを読み解いて答える、ということです。
自信のない人もいるかもしれませんが、とりあえずやってみましょう。



```js
const originalArray = [1, 2, 3]
const state = {
  array: originalArray
}

console.log(originalArray === state.array) // 問1

console.log(originalArray === [1, 2, 3]) // 問2

originalArray.push(4)

console.log(originalArray) // -> [1, 2, 3, 4]
console.log(state.array) // 問3

console.log(originalArray === state.array) // 問4

state.array = originalArray.filter(v => v !== 2)

console.log(state.array) // 問5
console.log(originalArray) // 問6

console.log(originalArray === state.array) // 問7
```

## 解答
解き終わったらスクロールしてください。

<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>

```js
/*
問1 true
問2 false
問3 [1, 2, 3, 4]
問4 true
問5 [1, 3, 4] 
問6 [1, 2, 3, 4]
問7 false
*/
```

# 解説
## 問1
私たちが普段行う、
```js
const temp = 1
```
というような変数宣言を行うとき、JavaScriptでは、

- tempのためのメモリを割り当てる
- 割り当てられたメモリに、1という値を格納する

という処理が行われます。ですから、例えば、
```js
console.log(temp)
```
とログ出力を行うときは、

- `temp`に割り当てられたメモリに行き、
- そのメモリの中の`1`というデータを読む

という処理が行われます。この、「メモリのどこに割り当てられたか」を、「**番地**」と呼んだりします。

ここで、問題文のコードをもう1度見てみましょう。
```js
const originalArray = [1, 2, 3]
const state = {
  array: originalArray
}

console.log(originalArray === state.array) // ①
```
`originalArray`という配列を定義してから、`state`というオブジェクトの`array`というキーの値に、`originalArray`を入れています。
この``originalArray``と`state.array`が「**[等しい](https://jsprimer.net/basic/operator/#strict-equal-operator)**」のかどうか？というのが①の問題です。

まず、`originalArray`と`state`の番地は異なります。この2人は違う番地に住んでいるのです。

では、`state.array`、つまり、`state`に格納された`array`はどうでしょうか。面白いことに、`state.array`の番地は、`originalArray`と同じになるのです。こういうケースを、

- `state.array`は`originalArray`を参照している
- `state.array`の参照・参照先は`originalArray`である

などと言います。

JavaScriptの`===`は、「参照が同じかどうか」、つまり「番地が同じかどうか」をチェックしている(注)ので、`originalArray === state.array`は`true`になります。

注) オブジェクトの場合。参考サイト: https://jsprimer.net/basic/operator/#strict-equal-operator

## 問2
では、次のケースはどうでしょうか。
```js
const originalArray = [1, 2, 3]
console.log(originalArray === [1, 2, 3]) // ②
```
`originalArray`と`[1, 2, 3]`、同じ要素を持った配列を比較しています。この2つの番地は同じになるでしょうか？

答えはNOです。`originalArray === [1, 2, 3]`の右辺、`[1, 2, 3]`には、`originalArray`とは別の番地が割り当てられます。

ですから、`originalArray === [1, 2, 3]`は`false`になります。中身は一緒でも、この2つは別物だと言うことですね。

## 問3
```js
const originalArray = [1, 2, 3]
const state = {
  array: originalArray
}
originalArray.push(4)
console.log(originalArray) // -> [1, 2, 3, 4]
console.log(state.array) // 問3
```
今度は、`originalArray`に値が`push`されています。この時、`state.array`はどうなるでしょうか。

`originalArray`に値が`push`されても、`originalArray`の番地は変わりません。以前のままです。格納されているモノが`[1, 2, 3]`から`[1, 2, 3, 4]`へと変化しただけです。

`state.array`は、`originalArray`を参照しているので、「`state.array`を読みに行く」とは、「参照先の`originalArray`の番地を読みに行く」というのと同じです。ですから、出力結果は`originalArray`と同じく`[1, 2, 3, 4]`になります。

## 問4
```js
const originalArray = [1, 2, 3]
const state = {
  array: originalArray
}
originalArray.push(4)
console.log(originalArray === state.array) // 問4
```
問4は、実は問3を考える段階で既に答えが出ています。`push`しても`originalArray`の番地は変わらずでしたから、依然として`originalArray`と`state.array`の番地は同じです。

ですから、`===`で「番地が同じかどうか」を比較した結果は`true`になります。

## 問5, 問6

```js
const originalArray = [1, 2, 3]
const state = {
  array: originalArray
}

originalArray.push(4)
state.array = originalArray.filter(v => v !== 2)

console.log(state.array) // 問5
console.log(originalArray) // 問6
```

- `originalArray`に値を`push`する
- `state.array`に、`originalArray`を`filter`した結果を入れている

という2つの操作が行われています。少しややこしいですね。

1つずつ見ていきましょう。まずは`push`です。これは問4と同じですね。
```js
originalArray.push(4)
console.log(originalArray) // -> [1, 2, 3, 4]
console.log(state.array) // 問3: [1, 2, 3, 4]

console.log(originalArray === state.array) // 問4: true
```
この段階では、`originalArray`の番地と`state.array`の番地は同じです。

では次に、これに`filter`するとどうなるでしょうか。

`filter`をちゃんと理解していなくても、問5だけは分かるかもしれません。2を除外していますから`[1, 3, 4]`になりますね。

少し難しいのは、問6、つまり`filter`したあとの`originalArray`がどうなるか、でしょう。これを考えるには、`filter`がどういうメソッドなのかを理解する必要があります。

[こちらのサイト](https://jsprimer.net/basic/array/#array-filter)の、`filter`の説明を読んでみましょう。

> Arrayのfilterメソッドは配列の要素を順番にコールバック関数へ渡し、コールバック関数がtrueを返した要素だけを集めた新しい配列を返す非破壊的なメソッドです。 配列から不要な要素を取り除いた配列を作成したい場合に利用します。

この「新しい配列」というのがポイントです。「新しい」というのは、「参照が異なる」「番地が異なる」という意味です。問題文に置き換えると、「`originalArray`の中から`2以外`の要素だけを集めた、**`originalArray`とは番地の異なる**配列を返す」ということです。

重要なのは、元の`originalArray`の番地には何もしていない、ということです。ですから、`originalArray`は元の`[1, 2, 3, 4]`のまま、変わっていません。元の番地はそのままにして、新しい番地を`filter`するときに割り当てているのです。

このように、元の配列を変えずに新しい配列を返すメソッドを`非破壊メソッド`といいます。

## 問7
```js
const originalArray = [1, 2, 3]
const state = {
  array: originalArray
}


originalArray.push(4)
state.array = originalArray.filter(v => v !== 2)

console.log(state.array) // 問5: [1, 3, 4]
console.log(originalArray) // 問6: [1, 2, 3, 4]

console.log(originalArray === state.array) // 問7
```
集大成です。`state.array`には、`originalArray`を`filter`した**新しい**配列が入っています。ということは、番地も新しくなっているということです。

ですから、`originalArray === state.array`は`false`になります。参照が変わってしまいましたからね。

## おまけの追加問題
理解度チェックのために、追加問題を出します。分かりますか？
```js
const originalArray = [1, 2, 3]
const state2 = originalArray.filter(v => v)

console.log(originalArray) // => [1, 2, 3]
console.log(state2) // => [1, 2, 3]

console.log(originalArray === state2) // 追加問題
```

<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>

### 解答

```js
const originalArray = [1, 2, 3]
const state2 = originalArray.filter(v => v)

console.log(originalArray === state2) // =>false
```
この2つの配列は、入っている要素の値は同じですが、参照が異なります。`filter`は「新しい配列」を返すのでしたね。

# まとめ
どうだったでしょうか。「参照」と仲良くなれましたか？

より深く学びたい人は、以下のようなキーワードで検索してみてください。

- [シャローコピー](https://developer.mozilla.org/ja/docs/Glossary/Shallow_copy)
- [ディープコピー](https://developer.mozilla.org/ja/docs/Glossary/Deep_copy)
- [メモリ管理](https://developer.mozilla.org/ja/docs/Web/JavaScript/Memory_Management)

***

「**Promiseを完全に理解できるかもしれないクイズ**」もあります。よろしければぜひ。

https://qiita.com/k5mrt/items/3c5868371bda36e6ca66