関数型言語に爪先レベルで入門したので、まずはJavaScriptで値をイミュータブル（不変）に扱ってみる

[Mikatus Advent Calendar 2019](https://qiita.com/advent-calendar/2019/mikatus) 3日目の記事。

# 最初に
こんにちは。

普段はTypeScriptでVue.jsを書いているのですが、関数型言語を勉強しようと、最近社内で`すごいHaskellたのしく学ぼう！`通称`すごいH本`の勉強会を立ち上げました。

Haskellの特徴は色々あると思いますが、カリー化とか部分適用とか、JavaScriptに活かすのはなかなか難しそうだなと感じるものが多いので、わかりやすく効果がありそうな、`値をイミュータブルに扱う方法`について紹介していきます。

私自身JavaScriptも入門レベルなので、紹介する内容は基礎的なものですが、同じように最近JavaScriptに入門した人がいたら参考にしてみてください。

では参ります。

# イミュータブルのメリット
まずイミュータブルとは、Wikipediaさんによると、
> イミュータブル (英: immutable) なオブジェクトとは、作成後にその状態を変えることのできないオブジェクトのことである。
[イミュータブル](https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%9F%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%96%E3%83%AB) - Wikipedia

らしいです。

プログラミング関係の言葉をWikipediaで調べると難解なことが多いのですが、これは比較的わかりやすいですね。

この定義ではわかりづらいという場合でも、後に出てくる実際のコードを見ればなんとなく理解できるのではないかと思います。

で、イミュータブルにプログラミングするメリットですが、

- _不測の値が混入するのを防ぐ_
- _値が置き換えられてないかいちいち確認する手間や心理的負荷がなくなる_
- _プログラムの保守性が上がる_

あたりが考えられ、特にチームで開発する上では大切なことだと感じています。

私自身独学期間を経て、今年エンジニアになったばかりなのですが、一人で開発する時には意識することもなかった、他のメンバー・将来のメンバーの負荷を減らすコードの必要性を実感しています。

というわけで、例外はあると思いますが、基本的に値はイミュータブルな方が良いのではないでしょうか。

では、これらのメリットを享受するため、JavaScriptでは実際どう書くのかを3つ見ていきましょう。

# 再代入しない
第一に、_一度変数を宣言したら、再代入しない_ということです。

JavaScriptでは変数を宣言する時に、`var`、`let`もしくは`const`が使えます。

`var`はよっぽどの例外がない限り、使わないという方向で問題ないでしょう。
私にはよっぽどの例外が思いつきません。

残るは、`let`と`const`ということになりますが、

- _letは再代入可能_
- _constは再代入不可_

になります。

実際のコードで見ていきましょう。

まずは`let`で変数を宣言した場合です。

```javascript:let.js
let firstName = 'Ai'
let lastName = 'Katou'

firstName = 'Kai'
lastName = 'Atou'

let fullName = `${firstName} ${lastName}`
console.log(fullName)
// Kai Atou
// 書き換えられている！
```
加藤あいが阿藤快に書き換えられてしまっています。

次に`const`です。

```javascript:const.js
const firstName = 'Lewis'
const lastName = 'Ann'

firstName = 'Rice'
// TypeError: Assignment to constant variable.
// 再代入しようとするとTypeErrorに

const fullName = `${firstName} ${lastName}`
console.log(fullName)
// Lewis Annが必ず表示される

const obj = { number: 11, firstName: 'Hinata', lastName: 'Kashiwagi' }

obj.number = 100

console.log(obj)
// {number: 100, firstName: 'Hinata', lastName: 'Kashiwagi'}
// あくまで再代入の禁止で、オブジェクトのプロパティ変更は可能なため注意
```
アン・ルイスを半ライスに書き換えようと思ったのですが、できませんでした。

というわけで変数の宣言には`const`を使っていきましょう。
JavaScriptを本格的に書き始めて２ヶ月弱ですが、`let`での宣言が必要になる機会はほぼないという印象です。

なお、`const`でもあくまで再代入ができなくなるだけであって、オブジェクトのプロパティは変更できてしまうので注意しましょう。

次行きます。

# 配列をイミュータブルに扱う
今度は配列をイミュータブルに扱う方法です。

配列の操作には`破壊的操作`と`非破壊的操作`が存在します。

配列に対して`破壊的操作`を行うと、操作を行なった配列が直接変更されてしまい、変更前の値を参照することができなくなります。

対して、`非破壊的操作`は元の配列はそのままに新しい配列を返すので、変更前の値も参照することができます。

実際のコードで見ていきましょう。

```javascript:array.js
const array = [3, 5, 6, 10, 11, 12]

const sortedArray = array.sort((a, b) => b - a)

console.log(array)
// [ 12, 11, 10, 6, 5, 3 ]
// 元の配列もソートされてしまっている！
```

sortメソッドは`破壊的操作`なので元の配列もソートされてしまいます。
sortメソッドは便利なので、ぜひ使いたいところですが、このままではイミュータブルな状態を実現できません。

なので下記のように書いていきましょう。

```javascript:array.js
const array = [3, 5, 6, 10, 11, 12]

const copiedArray = [...array]
// 元のarrayを変更しないようにスプレッド構文で一旦コピーする

const sortedArray = copiedArray.sort((a, b) => b - a)

console.log(array)
// [ 3, 5, 6, 10, 11, 12]
// ソート前の配列も呼び出せる

console.log(sortedArray)
// [ 12, 11, 10, 6, 5, 3 ]
```

sortメソッドを適用する前に配列をコピーしました。
一手間かかるだけのように思えますが、こうすることで上に挙げたメリットを享受できます。


```javascript:array.js
const array = [3, 5, 6, 10, 11, 12]

array.push(13)
// pushも元の配列を変更する

const addedArray = [...array, 13]
// スプレッド構文を使って要素が追加された新しい配列を作る
```
ここでは`sort`と`push`メソッドを例に挙げていますが、破壊的操作は他にもあります。

配列にメソッドを適用しようと思ったら、それが`破壊的`か`非破壊的`かを確認してみましょう。

次行きます。

# オブジェクトをイミュータブルに扱う
配列に続いて今度はオブジェクトです。

再代入しないの項目でも少し触れましたが、オブジェクトについては`const`で宣言しようとプロパティの変更ができてしまいます。

ここもイミュータブルに扱いたいところです。

実際のコードで見ていきましょう。

```javascript:object.js
const obj = { number: 11, firstName: 'Hinata', lastName: 'Kashiwagi' }

obj.color = 'orange'
// obj[color] = 'orange' も同様

console.log(obj)
// { number: 11, firstName: 'Hinata', lastName: 'Kashiwagi', color: 'orange' }
// 変更されてしまっていて、変更前の状態が参照できない

const addedObj = { ...obj, color: 'orange' }
// スプレッド構文を使って追加する

console.log(addedObj)
// { number: 11, firstName: 'Hinata', lastName: 'Kashiwagi', color: 'orange' }

console.log(obj)
// { number: 11, firstName: 'Hinata', lastName: 'Kashiwagi' }
// 元の状態も参照できる
```

元のオブジェクトのプロパティを直接変更するのではなく、`スプレッド構文`を使って新しいオブジェクトを作っています。

`スプレッド構文`便利ですね。

- [【JavaScript】スプレッド構文の便利な使い方まとめ](https://qiita.com/Nossa/items/e6f503cbb95c8e6967f8)

ただ、この例については、TypeScriptを導入しているなら、型をちゃんと定義することで、後からプロパティの追加とかができないようにする方がいいと思います。

#最後に
値をイミュータブルに扱う方法を３つ紹介しました。

意識したことがなかった人がいたら、ぜひ意識して今回紹介した方法などを試してみてください。

すごいH本勉強会では、まだモナド等には触れていないので、万が一モナドを理解できたらJavaScriptを書く際にも活かせないか模索したいと思います。

それでは！