コールバック関数について

まだイマイチ使いどころが分からない「コールバック関数」についてまとめました。

```js
// これがコールバック関数として使われる
function greeting(name) {
  alert(`Hello, ${name}`);
}

// 別の関数（（コールバック）を引数として受け取っている
function processUserInput(callback) {
  const name = prompt("please enter your name.");
  callback(name);  // ここでコールバック関数を実行
}

// greetingをコールバック関数として渡している
processUserInput(greeting);
```
- 関数を別の関数の引数として渡すことができる
- 渡された関数は、必要なタイミングで実行される
- データの処理や非同期処理でよく使用される

```js
// 配列の各要素に対して処理を行う例
const numbers = [1, 2, 3, 4, 5];

// コールバック関数を使用する一般的なメソッド
numbers.forEach(function(number) {
  console.log(number * 2);
});

// 非同期処理の例
function downloadData(url, successCallback, errorCallback) {
  // データのダウンロードを想定
  if (/* ダウンロード成功 */) {
    successCallback('データ');
  } else {
    errorCallback('エラーが発生しました');
  }
}

// 使用例
downloadData(
  'https://api.example.com/data',
  function(data) { console.log('成功:', data); },
  function(error) { console.log('エラー:', error); }
);
```

## イベントハンドリング
コーダーには見慣れた下記もコールバック関数らしく、第二引数として渡している関数がコールバック関数なのだそうです。
```js
button.addEventListener('click', function() {
  console.log('ボタンがクリックされました');
});
```
下記も内容は同じ。
```js
//関数を別で定義
function handleClick() {
  console.log('ボタンがクリックされました');
}
button.addEventListener('click', handleClick);

//アロー関数を使う方法
button.addEventListener('click', () => {
    console.log('ボタンがクリックされました');
});
```

同様の例
```js
const button = document.querySelector('button');

// クリック回数を数えるコールバック関数の例
let clickCount = 0;
button.addEventListener('click', function() {
  clickCount++;
  console.log(`ボタンが${clickCount}回クリックされました`);
});

// 複数のイベントを設定する例
button.addEventListener('mouseenter', function() {
  console.log('マウスがボタンの上に乗りました');
});

button.addEventListener('mouseleave', function() {
  console.log('マウスがボタンから離れました');
});
```

## 配列操作
```js
const numbers = [1, 2, 3, 4, 5];
// mapのコールバック関数
const doubled = numbers.map(num => num * 2);
// 結果: [2, 4, 6, 8, 10]

// filterのコールバック関数
const filterd = numbers.filter(num => num > 2);
// 結果: [3, 4, 5]
```

内部ではこのような処理
```js
const doubled = numbers.map(num => num * 2);
// これは以下のような処理を行っています：
// 1. [1] → コールバック関数実行 → 2
// 2. [2] → コールバック関数実行 → 4
// 3. [3] → コールバック関数実行 → 6
// 4. [4] → コールバック関数実行 → 8
// 5. [5] → コールバック関数実行 → 10

const filtered = numbers.filter(num => num > 2);
// これは以下のような処理を行っています：
// 1. [1] → コールバック関数実行 → false → 含まない
// 2. [2] → コールバック関数実行 → false → 含まない
// 3. [3] → コールバック関数実行 → true  → 含む
// 4. [4] → コールバック関数実行 → true  → 含む
// 5. [5] → コールバック関数実行 → true  → 含む
```

明示的に書くと
```js
const doubled = numbers.map(function(num) {
    return num * 2;
});

const filterd = numbers.filter(function(num) {
    return num > 2;
});
```

似たような配列のメソッドも同様にコールバック関数を使うそうです
```js
numbers.forEach(num => console.log(num));

const found = numbers.find(num => num > 3);

const sum = numbers.reduce((acc, num) => acc + num, 0);
```

## タイマー処理
```js
setTimeout(function() {
    console.log();
}, 3000);
```
この場合、setTimeoutは2つの引数を受け取ります。

第1引数：指定時間後に実行される関数（コールバック関数）
第2引数：待機する時間（ミリ秒）

JavaScriptは指定された時間（3000ミリ秒）待機します
時間が経過したら、自動的にコールバック関数を実行します

いろいろな書き方
```js
// 関数を別で定義する方法
function delayedMessage() {
  console.log('3秒後に実行されます');
}
setTimeout(delayedMessage, 3000);

// アロー関数を使う方法
setTimeout(() => {
  console.log('3秒後に実行されます');
}, 3000);

// 複数の処理を行う例
setTimeout(() => {
  console.log('3秒経過しました');
  console.log('追加の処理も実行できます');
  alert('完了！');
}, 3000);
```

setIntervalもコールバック関数を使用します
```js
//1秒ごとに実行
setInterval(() => {
    console.log('1秒経過');
}, 1000);

//カウントアップ
let count = 0;
count timer = setInterval(() => {
    count++;
    console.log(count);
    if (count >=5) {
        clearInterval(timer);//5回実行したらていし
    }
}, 1000);
```

### コールバック地獄を避ける
```js
// 避けるべき例
getData(function(a) {
  getMoreData(a, function(b) {
    getMoreData(b, function(c) {
      console.log(c);
    });
  });
});

// 代替案：Promiseを使用
getDate()
    .then(a => getMoreData(a))
    .then(b => getMoreData(b))
    .then(c => console.log(c));
```

### thisのコンテキストに注意する
```js
const user = {
    name: 'Taro',
    greet: function() {
        setTimeout(function() {
            console.log(this.name); //undefined
        });
        setTimeout(() => {
            console.log(this.name); //Taro
        });
    }
};
```
- 通常の関数の場合
```js
setTimeout(function() {
  console.log(this.name); // undefined
});
```
通常の関数は、呼び出し時に新しいthisコンテキストを作ります
setTimeoutの中のコールバック関数は、グローバルコンテキスト（ブラウザではwindow）で実行されます
そのため、thisはuserオブジェクトを参照せず、undefinedとなります

- アロー関数の場合：
```js
setTimeout(() => {
  console.log(this.name); // 'Taro'
});
```
アロー関数は自身のthisを作らず、外側のスコープのthisを引き継ぎます
この場合、greetメソッドのthis（つまりuserオブジェクト）を参照します
そのため、this.nameは正しく'Taro'を取得できます

- 別の解決方法
```js
const user = {
    name: 'Taro',
    greet: function() {
     // 方法1: thisを変数に保存
        const self = this;
        setTimeout(function() {
          console.log(self.name); // 'Taro'
        });
    // 方法2: bindを使用
        setTimeout(function() {
            console.log(this.name); // 'Taro'
        }.bind(this));
    }
}
```
※bindは関数のthisの値を永続的に固定するためのメソッド