JavaScript（ES6〜）関数と宣言

## 関数とは
ある一連の手続きをひとつのまとまった処理として扱う仕組み

## 関数宣言
`function`からはじまる文
```javascript
// 関数宣言
function 関数名(仮引数1, 仮引数2) {
    // 処理コード
    return 関数の返り値;
}

// 関数呼び出し
console.log(関数名(仮引数1, 仮引数2)); //  関数の返り値
```
`return`が実行されると、関数の処理はそこで終了し、それ以降のコードは実行されません
```javascript
function testReturn() {
    console.log("この行は実行される");
    return;
    console.log("この行は実行されない");
}

testReturn();
```

関数が値を返さない場合、`return`を省略してもよいです
```javascript
function sayHello() {
    console.log("Hello!");
}

sayHello(); //  Hello!
```

## 関数の引数
JavaScriptでは、関数を定義したときの仮引数の数と、実際に呼び出すときの引数の数が一致しなくても問題なく実行できます。
- TypeScriptでは、関数の引数を明示的に定義しないとエラーになります

### 引数が不足している場合
指定されなかった仮引数には`undefined`が自動的に設定されます
```javascript
function show(a, b) {
    console.log(`a: ${a}, b: ${b}`);
}

show(1, 2); // a: 1, b: 2
show(1);    // a: 1, b: undefined
```

### 仮引数に対して引数が多い場合
あふれた引数は無視されます
```javascript
function show(a, b) {
    console.log(`a: ${a}, b: ${b}`);
}

show(1, 2); // a: 1, b: 2
show(1, 2, 3);    // a: 1, b: 2
```

### デフォルト引数
関数の引数が渡されなかったときに、あらかじめ設定された初期値を使用する仕組み
```javascript
function add(a = 0, b = 0) {
    return a + b;
}

console.log(add(5, 10)); // 15
console.log(add(5));     // 5（b はデフォルト値 0）
console.log(add());      // 0（a, b ともにデフォルト値 0）
```
OR演算子( `||` )を使った方法もあるが、falsyな値を渡すとデフォルト値が入ってしまうなどのバグの原因になるため、基本的にデフォルト引数を使うこと
```javascript
function add(a, b) {
    a = a || 0;
    b = b || 0;
    return a + b;
}

console.log(add(5, 10)); // 15
console.log(add(5));     // 5（b はデフォルト値 0）
console.log(add());      // 0（a, b ともにデフォルト値 0）
console.log(add(undefined, 10)); // 10（undefined のつもりが `0 || 0` でデフォルト値が適用）
```

## 可変長引数
関数が受け取る引数の数を固定せず、可変個の引数を扱える仕組み
### 残余引数(Rest parameters)
関数の仮引数に`...`を使い、余った引数を配列として受け取る仕組み
```javascript
function showNumbers(first, ...rest) {
    console.log("First:", first);
    console.log("Rest:", rest);
}

showNumbers(1, 2, 3, 4, 5);
// First: 1
// Rest: [2, 3, 4, 5]
```
#### Spread構文との違い
スプレッド構文は配列の要素を展開して個別の引数として渡すのに対し、残余引数は余った引数をまとめて配列として受け取します
```javascript
function fn(a, b) {
    console.log(a); // => 1
    console.log(b); // => 2
}

const array = [1, 2];

// スプレッド構文で配列を展開
fn(...array);
```

### arguments
仮引数があってもなくても、関数に渡されたすべての引数を格納する特殊なオブジェクト
アロー関数内では使えないことや、関数が可変長を受けるのかを仮引数だけを見て判断できないなどの可読性の問題などにより、`arguments`を使わないことを推奨されています
```javascript
// 仮引数がない場合
function showArguments() {
    console.log(arguments);
    console.log(arguments[0]); // 最初の引数
    console.log(arguments[1]); // 2番目の引数
}

showArguments(1, 2, 3);
// (3) { '0': 1, '1': 2, '2': 3 }
// 1
// 2

// 仮引数がある場合
function fn(a, b) {
    console.log(a);        // 仮引数 a に対応する引数
    console.log(b);        // 仮引数 b に対応する引数
    console.log(arguments); // すべての引数 (仮引数も含む)
}

fn(1, 2, 3, 4); 
// 1
// 2
// (4) { '0': 1, '1': 2, '2': 3, '3': 4 }
```

## 関数の引数と分割代入
分割代入とはオブジェクトや配列の値を個別の変数に分割して代入する構文
```javascript
// オブジェクトの場合
function greet({ name, age }) {
    console.log(`Hello, my name is ${name} and I am ${age} years old.`);
}

const person = { name: 'Alice', age: 30 };
greet(person); //  "Hello, my name is Alice and I am 30 years old."

// 配列の場合
function display([x, y]) {
    console.log(`x: ${x}, y: ${y}`);
}

const coordinates = [10, 20];
display(coordinates); // => "x: 10, y: 20"
```

## 関数はオブジェクトの一種
- 関数名に`()`を付けて実行することで、関数の処理が開始されます
- 関数名に`()`を付けずに書くと、関数自体をオブジェクトとして参照することができます
```javascript
function greet() {
    console.log("Hello, World!");
}

// 関数`greet`を`sayHello`変数に代入している(オブジェクトとしてsayHelloに代入)
const sayHello = greet;
sayHello();
```

### 関数式
関数を変数に割り当てる式
#### 匿名関数(無名関数)
関数に名前を付けずに定義された関数
通常、関数式として使用され、関数名がないため、その場で使い捨ての関数として利用されます
```javascript
// 無名関数を変数に代入する場合
const greet = function() {
    console.log("Hello, World!");
};

greet(); // => "Hello, World!"

// 無名関数を引数として渡す場合
setTimeout(function() {
    console.log("1秒経過しました！");
}, 1000);
```

### アロー関数
`=>`を使って、無名関数を定義する構文(うわがきふか)
```javascript
const functionName = (param1, param2) => {
    // 処理
    return result;
};
```
#### 特徴
- 簡潔な構文
- 自身の`this`を持たないが、定義された 親スコープの`this`を引き継ぐ
- `arguments`オブジェクトを持たない
- コンストラクタ関数 として使用できないため、`new`キーワードで呼び出せない
- 関数本体が 1行だけ の場合、`return`を省略して暗黙的に返り値を返すことができる

```javascript
// newキーワードで呼び出せない
const MyClass = () => {};
const obj = new MyClass(); // エラー: MyClassはコンストラクタではない

// returnの省略
const multiply = (a, b) => a * b; // `return` は省略できる
```

### 同じ名前の関数宣言は後ろで宣言された関数に上書きされる
上書きされる条件
- `function`を使った関数宣言
- `var`を使った関数式

よって、上記の回避方法
- 関数宣言はアロー関数で行う
- 関数式には`const`または`let`を使う

```javascript
function greet() {
    console.log("Hello from first function!");
}

function greet() {
    console.log("Hello from second function!");
}

greet(); // => "Hello from second function!"
```

## コールバック関数
高階関数に引数として渡され、その関数が処理を終えた後に実行される関数
```javascript
// 高階関数: 引数として渡された関数を実行する関数
function runCallback(callback) {
    callback("Hello, world!");
}

// コールバック関数
const printMessage = (message) => {
    console.log(message);
};

// 高階関数にコールバック関数を渡す
runCallback(printMessage); // => "Hello, world!"
```

## メソッド
オブジェクトに関連付けられた関数
```javascript
const person = {
    name: "Alice",
    age: 25,
    // メソッドを定義
    greet: function() {
        console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
    }
};

// メソッドを呼び出す
person.greet();  // => "Hello, my name is Alice and I am 25 years old."
```

### メソッドの短縮表記法
```javascript
const person = {
    name: "Alice",
    age: 25,
    // メソッドの短縮表記法
    greet() {
        console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
    }
};

// メソッドを呼び出す
person.greet();  // => "Hello, my name is Alice and I am 25 years old."
```

## まとめ
上記の中でよく使われるものはアロー関数、分割代入、デフォルト引数、残余引数などがあります。特に、関数は上書き不可なアロー関数を使った方がよいです。理由はfunctionやvarを使った場合、同じ名前の関数宣言を上書きしてしまい、不具合の原因になるからです