フロント開発で役に立つ、JavaScriptの関数についてのあれこれ

## はじめに
日々フロント開発やコードレビューを行うなかで、「JavaScriptの関数について改めて復習、整理しておこう」と感じたので、まとめてみました。
フロント開発に関わっている方や、フロント開発を勉強中の方に読んでいただけたらと思います。

## JavaScriptの関数の様々な仕組みや使用方法

### アロー関数

従来の``function``ではなく、``=>``を用いた関数宣言。

- 匿名関数
- 引数が一つの場合は``()``が省略可能
- 単一式の場合は、``{}``や``return``を省略できる

```js
// 従来の関数
function (a, b){
  return a + b + 100;
}

// アロー関数
(a, b) => a + b + 100;
```

:::note warn
**注意点**
thisやsuperへの結びつけを持たないので、メソッドとして使用することができなかったり、コンストラクターとしても使用することは出来ません。
:::

### オブジェクトのプロパティに関数を入れる

関数はオブジェクトのプロパティに入れることができます。

```ts
const counter = {
  count: 0,
  up: () => counter.count++,
  down: () => counter.count--,
  show: () => console.log(counter.count)
}

counter.show() //=> 0
counter.up()
counter.show() //=> 1
```

:::note warn
**注意点**
上記の場合、countは外部から上書きすることが可能なため、安全性を考慮すると、後述する**クロージャ**を使用するのが望ましいです。
:::

### 関数にプロパティを持たせる

JavaScriptの関数はオブジェクトでもあるため、プロパティを持たせることができます。

```ts
const f = () => {
  console.log('aaa')
}

f.test = 'abc'

console.log(f.test) //=> abc
```

### デフォルト引数

関数の引数は、通常、省略すると`undefined`となるが、`デフォルト引数`を設定しておくことで、引数が省略されるかundefinedのときに、その値が代入される。

```ts
const multiply = (a, b = 2) => {
  console.log(a * b);
}

multiply(5, 3); // 15

multiply(5); // 10
```

### 残余引数(可変長引数)

引数の数が決まってないものを一般的に`可変長引数`といい、JavaScriptでは`残余引数`と呼ばれる。
`...(スプレッド構文)`を使用することで、複数の引数を一つの配列で受け取ることができる。

```ts

const f1 = (...params) => {
  console.log(params)
}

f1(1, 2, 3, 4) //=> [1, 2, 3, 4]

const f2 = (a, b, ...params) => {
  console.log(a, b, params)
}

f2(1, 2, 3, 4) //=> 1, 2, [3, 4]
```

:::note warn
**注意点**
残余引数が使用できるのは最後の引数だけです。また、複数の残余引数を持つことはできません。
:::

### 分割代入引数

JavaScriptには、オブジェクトのプロパティを抽出する`分割代入構文`があり、これは、オブジェクトの一部を切り出すときに便利な書き方で、関数の引数としても使用できる。
引数が複数の関数を作る際に、`分割代入引数`を使用することで、可読性を上げることができる。

```ts
// 分割代入の例
const obj = {a: 1, b: 2, c: 4}

// 分割代入
const {a, b} = obj

console.log(a) // 1
console.log(b) // 2
```

```ts
// 分割代入引数の例
const f = ({a, b}) => {
  console.log(a + b)
}

const obj = {a: 1, b: 2}

f(obj) //=> 3
```

```ts
// 分割代入引数にデフォルト引数を使用する例
const f = ({a = 1}) => {
  console.log(a)
}

f({}) //=> 1
f({a = undefined}) //=> 1
f({a = 2}) //=> 2
```

### 即時関数
`即時関数`とは作られてすぐに呼び出される関数のことです。
即時関数のメリットとしては、**1回だけ必要な処理を、関数外に影響を与えず実行できる**ということです。
使用するタイミングは少ないですが、**変数同士の依存関係を明確**にし、可読性を向上させることができます。


```ts
(() => {
  const a = 1
  const b = 2
  console.log(a + b)
})()
```

:::note warn
**注意点**
処理によっては即時関数を使用することで、逆に可読性を下げてしまうこともあるので、使用には見極めが必要です
:::

### 高階関数

`高階関数`とは**関数を引数に受け取る関数**、もしくわ**関数を返す関数**のことです。
また、高階関数に引数として渡す関数のことを`コールバック関数`と言います。
高階関数を使用するメリットとしては、**処理の責務の分離**や**可読性の向上**があります。
JavaScriptにはデフォルトで多数の高階関数が定義されていますが、ここでは一例として`map`を紹介します。
`map`以外の高階関数は、**開発現場でよく使う便利な関数**の項目に記述しています。

```ts
const array1 = [1, 4, 9, 16];

const array2 = array1.map(x => x * 2);

console.log(array2); //=> [2, 8, 18, 32]
```

### カリー化

`カリー化`とは、多引数関数を**関数を返す関数**(高階関数)を使用して実現することです。
特定の引数が固定になっている処理に適用することで、冗長な記述を削減し、可読性を上げることができます。
また、関数として分割しているので、個々の関数の再利用がしやすくなります。

```ts
// カリー化なしの例

const f = (lang, msg) => {
  console.log(`${lang}は、${msg}！`)
}

f('JavaScript', '楽しい') //=> JavaScriptは楽しい！
f('JavaScript', '簡単') //=> JavaScriptは簡単！
f('JavaScript', '最高') //=> JavaScriptは最高！
```

```ts
// カリー化ありの例

const f = (lang) => (msg) => {
  console.log(`${lang}は、${msg}！`)
}

const js = f('JavaScript')

js('楽しい') //=> JavaScriptは楽しい！
js('簡単') //=> JavaScriptは簡単！
js('最高') //=> JavaScriptは最高！
```

### クロージャ

`クロージャ`とは、**関数と関数定義時のコンテキストへの参照をセットにした特殊なオブジェクト**です。
JavaScriptでは、関数を作成すると裏側でクロージャが作成されます。
また、その関数が消失すると、クロージャも消失します。
この性質を利用して、関数内部の変数をプライベートメンバのように扱い、**関数に状態を持たせた、一種の記憶装置のような仕組み**を作ることができるのです。
`React`の`useState`もこの性質を利用して作られています。

```ts
const createCounter = () => {
  let cnt = 0

  const countUp = () => {
    cnt = cnt + 1
    console.log(cnt)
  }

  return countUp
}

const counterA = createCounter()
const counterB = createCounter()

counterA() //=> 1
counterA() //=> 2
counterA() //=> 3

counterB() //=> 1
counterB() //=> 2
counterB() //=> 3
```

### 値渡しとオブジェクト

JavaScriptでは、変数に値を代入すると**値の参照**が入り、変数に値を再代入すると**値の参照**が置き換わります。
しかし、オブジェクトのプロパティに直接代入した場合は、**値の参照**が置き換わらないため、参照元の値まで変更されてしまいます。
この性質は、関数の引数に対しても適用されてしまいます。

```ts
// 変数に代入する場合

let a = 10
let b = a
b = 100
console.log(a) // 10
```

```ts
// オブジェクトのプロパティに代入する場合

let a = { value: 10 }
let b = a
b.value = 100
console.log(a) // { value: 100 }
```

```ts
// 関数の引数の場合

const f = (a) => {
  a.n = 2
}

const b = { n: 1 }
f(b)
console.log(b) //=> { n: 2 }
```
### 開発現場でよく使う便利な関数
#### forEach
与えられた関数を、配列の各要素に対して一度ずつ実行する。
単純に、配列の全ての要素に対して何かしらの処理をしたい場合に使用する。

```ts
const array1 = ['a', 'b', 'c'];

array1.forEach(element => console.log(element));
//=> a
//=> b
//=> c
```

#### map
与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列を生成する。
配列の中の要素に処理を加え、要素の値を変更したい場合に使用する。

```ts
const array1 = [1, 4, 9, 16];

const map1 = array1.map(x => x * 2);

console.log(map1); //=> [2, 8, 18, 32]
```

#### filter
与えられた関数によって、**true**と評価された要素のみの配列を生成する。
配列の中から特定の要素のみを抜き出したい場合に使用する。

```ts
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];

const result = words.filter(word => word.length > 6);

console.log(result); //=> ["exuberant", "destruction", "present"]
```

#### find
与えられた関数によって、**true**と評価された最初の要素のみを返す。
配列の中から特定の値を取得したい場合に使用する。

```ts
const array1 = [5, 12, 8, 130, 44];

const found = array1.find(element => element > 10);

console.log(found); //=> 12
```

#### reduce
配列のそれぞれの要素に対して、順番通りに、コールバック関数を呼び出す。その際、直前の要素における計算結果の返値を渡す。
配列の要素全ての和などを求めたい場合に使用する。

```ts
const array1 = [1, 2, 3, 4];
const reducer = (previousValue, currentValue) => previousValue + currentValue;

console.log(array1.reduce(reducer)); //=> 10
```

#### every
配列のそれぞれの要素に対して、与えられた関数で評価し、全て**true**と評価されたかどうかを**bool値**で返す。
複数の評価値が**true**になったときに何か処理をしたい場合などに使用できる。

```ts
const array = [1, 2, 3, 4, 5]

const everyTest = array.every((element) => element < 10)

console.log(everyTest) //=> true
```

#### some
配列のそれぞれの要素に対して、与えられた関数で評価し、1つでも**true**と評価されたかどうかを**bool値**で返す。
配列の中に目的の値があるかどうか調べるときなどに使用できる。

```ts
const array = [1, 2, 3, 4, 5]

const someTest = array.some((element) => element % 2 === 0)

console.log(someTest) //=> true
```

#### sort
配列の要素を並び替える関数。
デフォルトでは、要素を文字列に変換した後、**UTF-16コード単位**の昇順へと並び替えられる。
比較関数を指定することで、数値としての昇順、降順に並び替えも可能。

```ts
const numbers = [20, 10, 1, 22, 3, 5]

// デフォルト
numbers.sort()
console.log(numbers) //=> [1, 10, 20, 22, 3, 5]

// 数値の昇順
numbers.sort((a, b) => a < b ? -1 : 1)
console.log(numbers); //=> [1, 3, 5, 10, 20, 22]

// 数値の降順
numbers.sort((a, b) => a > b ? -1 : 1)
console.log(numbers); //=> [22, 20, 10, 5, 3, 1]
```

#### includes
配列の中に指定した要素が含まれているかを**bool値**で返す関数。

```ts
const array = ['a', 'b', 'c']

console.log(array.includes('c')) //=> true
console.log(array.includes('d')) //=> false
```

#### Object.keys
オブジェクトに存在する**key**のみの配列を生成する関数。

```ts
const obj = { a: 1, b: 2, c: 3 }

console.log(Object.keys(obj)); //=> ['a', 'b', 'c']
```

#### Object.values
オブジェクトに存在する**value**のみの配列を生成する関数。

```ts
const obj = { a: 1, b: 2, c: 3 }

console.log(Object.values(obj)); //=> [1, 2, 3]
```

#### Object.entries
オブジェクトに存在する**key**・**value**のペアで配列を生成する関数。

```ts
const obj = { a: 1, b: 2, c: 3 }

console.log(Object.entries(obj)); //=> [['a', 1], ['b', 2], ['c', 3]]
```

#### Object.fromEntries
key・valueのペアからオブジェクトを生成する関数。

```ts
const user = Object.fromEntries([["id", 1], ["name", "Taro"]])

console.log(user) //=> {id: 1, name: "Taro"}
```

## おわりに
ここまでお読みいただきありがとうございました。
記述ミスやわかりにくい点、追加した方が良い項目などございましたら、お手数ですが、コメントや編集リクエストなどで送っていただけたら幸いです。

## 参考にさせていただいた書籍

https://www.amazon.co.jp/Software-Design-%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3-2022%E5%B9%B402%E6%9C%88%E5%8F%B7-%E9%9B%91%E8%AA%8C-ebook/dp/B09QKG3C1L/ref=sr_1_13?crid=2U7VC9CZ70BI5&keywords=software+design&qid=1672293279&sprefix=soft%2Caps%2C171&sr=8-13