JavaScriptの関数について

# 基本編

## 関数とは

- ロジックの定義のこと
- 関数として定義しておくことで同じロジックを何度も書かなくてよくなる
- 関数を呼ぶだけでいつでも同じ処理を実行できちゃう

## 関数の宣言と実行

### 宣言

- JavaScriptでは関数の定義に`function`を使う
- 引数を受け取るための()をつける
- {}の中にロジックを書く

```js
// 名前がない関数は()で囲わないとエラーになる
(
  function() {
    console.log('hello');
  }
)
```

### 実行

- 関数の定義に()をつけることで実行される

```js
// 名前がない関数は()で囲わないとエラーになる
(
  function() {
    console.log('hello');
  }()
  // hello
)
```
- ※このように定義に直接()をつけて実行する関数のことを即時関数と呼ぶ
    - 定義して即時に実行されているので

### 関数に名前をつける

- 上記の例では定義を直接実行しているので、「関数を呼ぶだけでいつでも同じ処理を実行できちゃう」とはならない
- なので関数の定義に名前をつける

```js
function sayHello() {
  console.log('hello');
}
```
- 関数名に()をつけると実行できる
- これで一度定義すれば呼び出すだけで何度でも実行できる

```js
sayHello(); // hello
sayHello(); // hello
sayHello(); // hello
```

## 関数式

- JavaScriptでは関数を変数に代入することができる
- 変数に入れる時は関数に名前をつけなくても平気
- 実行は変数名に()をつける

```js
var sayHello = function() {
  console.log('hello!');
}
sayHello(); // hello!
```

- sayHelloという変数に関数の定義が入っているので、「関数の定義に()をつけることで実行される」の形になっている

```js
sayHello();
// ↑は↓が実行されているということ
function() {
  console.log('hello!');
}()
```

## アロー関数

- ES2015から使えるようになった関数の宣言方法
- `function()`が`() =>`に置き換わるイメージ
- アロー関数には名前をつけられないので変数に代入して実行する

```js
const sayHello = () => {
  console.log('hello!!');
}
sayHello(); // hello!!
```

- 処理が一行だけの時は{}を省略して書くことができる

```js
const sayHello = () => console.log('hello!!');
```
- これまでのルールと同様で関数の定義に直接()をつけて実行することもできる

```js
(() => console.log('hello!!'))();
// hello!!
```

## まとめ

- 関数は定義に()をつけると実行できる
- 関数の定義に名前をつけることができる
- 関数の定義を変数に代入できる
- アロー関数を使うと省略して書ける


# 応用編

## 関数を受け渡す

- 関数は定義に()をつけることで実行できる
- なので定義を変数に入れてたらい回しにして実行したい時に()をつける、なんていう使い方ができる

### サンプル

```js
var sayHello = function() {
  console.log('hello!!!');
}

var greet = function(someMethod) {
  console.log('greet!');
  someMethod();
}

// 関数の定義を引数で渡す
greet(sayHello);
// greet
// hello!!!
```

- sayHelloとgreetの2つの関数がある
    - sayHelloはコンソールにhello!!!と出すだけ
    - greetはコンソールにgreetと出した後、引数で受け取った関数を実行している
- greetの実行
    - 引数にsayHello(=関数の定義)を渡している
        - ここでは定義を渡しているだけ()がついていないのでsayHelloは実行されない
- greet実行時の動き
    - まずコンソールにgreetが表示される
    - 次に引数で受け取った関数を実行し、コンソールにhello!!!と表示される
    - someMethodの中身は実行時に渡されたsayHelloで、sayHelloの中身は関数の定義

    ```js
    someMethod()
    // ↑は↓が実行されているということ
    function() {
      console.log('hello!!!');
    }()
    ```

## classを使うケース

- classはES２０１５から使える書き方(今回は必要なところしかふれない)

### クラスメソッドの作り方

- classの中に`メソッド名()`と書くことでメソッドを作ることができる
- 実行する時はnewしたインスタンスに対してメソッドを呼ぶ

```js
class Sample1 {
  sayHello() {
    console.log('hello');
  }
}
var sample1 = new Sample1();
sample1.sayHello(); // hello
```

## thisの変化

### thisが変わってエラーになるパターン

- メソッドの中のthisはメソッドを呼び出した時の`.`の前の値
    - `sample.greet()`と呼んだ場合はthisはsampleになる
- メソッドの中でfunctionが出てくるとthisが変わってしまう

```js
class Sample2 {
  constructor() {
    this.members = ['Taro', 'Ichiro', 'Jiro'];
  }

  sayHello(name) {
    console.log(`hello ${name}`);
  }

  greet() {
    // sample2.greet()と呼んだのでthisはsample2(Sampleクラスのインスタンス)
    this.members.forEach(
      // functionの中に入るとthisが変わってしまう
      function(member) {
        // ここのthisはsample2(Sampleクラスのインスタンス)ではないのでsayHelloメソッドが見つからずエラーになる
        this.sayHello(member);
      }
    );
  }
}
var sample2 = new Sample2();
sample2.greet();
```

### bindを使ってthisをコントロールする

- thisが変わってエラーが出るのを防ぐために`bind`を使う
- 関数の定義に対して`.bind(xxx)`をつけると`xxx`の値がthisとして扱われる

```js
class Sample3 {
  constructor() {
    this.members = ['Taro', 'Ichiro', 'Jiro'];
  }

  sayHello(name) {
    console.log(`hello ${name}`);
  }

  greet() {
    // sample3.greet()と呼んだのでthisはsample3(Sampleクラスのインスタンス)
    this.members.forEach(
      // functionにbind(this)をつけている
      function(member) {
        // thisの値はsample3(Sampleクラスのインスタンス)なので問題なく動く
        this.sayHello(member);
      }.bind(this)
    );
  }
}
var sample3 = new Sample3();
sample3.greet();
// hello Taro
// hello Ichiro
// hello Jiro
```

### アロー関数でのthis

- アロー関数を使うとデフォルトで`.bind(this)`したのと同じ動きをする
    - より正確には、アロー関数はthisをもたないので１つ上のスコープのthisが使われるという動きをしている

```js
class Sample4 {
  constructor() {
    this.members = ['Taro', 'Ichiro', 'Jiro'];
  }

  sayHello(name) {
    console.log(`hello ${name}`);
  }

  greet() {
    this.members.forEach(
      (member) => {
        // thisの値はsample4(Sampleクラスのインスタンス)なので問題なく動く
        this.sayHello(member);
      }
    );
  }
}
var sample4 = new Sample4();
sample4.greet();
// hello Taro
// hello Ichiro
// hello Jiro
```

## まとめ

- 関数は定義に()をつけることで実行できるので、定義をたらい回しにして使いたい時に実行できる
- 関数/メソッドのなかに関数がある場合は`this`の変化に注意
- `bind`を使うと`this`の制御ができる
- アロー関数を使うと`this`の制御を勝手にやってくれる