[Day10] 関数の高度な機能 part2

本記事は一人アドベントカレンダー企画の一つです。
[30代未経験エンジニアが25日後にJavaScriptをマスターするカレンダー](https://qiita.com/advent-calendar/2022)

[JAVASCRIPT.INFO](https://ja.javascript.info/)を元にJavaScriptを勉強していき、そこで学んだ知識をアウトプットしていきます。
２５日でJAVASCRIPT.INFOをやりきり、未経験エンジニアがJavaScriptをマスターする過程を投稿していきます。



## 6.6 関数オブジェクト, NFE
### 知らない単語
- レキシカル環境とは
    - 環境レコードと、外側のレキシカル環境への参照がセットされたオブジェクトの事

### 学んだこと
関数は型ではなくオブジェクト
オブジェクトの中でメソッドも関数も持てる

- nameプロパティ

関数名は “name” プロパティとしてアクセス可能
```javascript
function sayHi() {
  alert("Hi");
}
alert(sayHi.name); // sayHi
```

- lengthプロパティ

スプレッド演算子がある時にlengthプロパティを使用すると
```javascript
function f1(a) {}
function f2(a, b) {}
function many(a, b, ...more) {}

alert(f1.length); // 1
alert(f2.length); // 2
alert(many.length); // 2
```
スプレッド演算子の箇所は数がわからないので、その前までの数をカウントする

- カスタムプロパティ

自分でプロパティを入れるという事
```javascript
function sayHi() {
  alert("Hi");

  // 何度実行したかカウント
  sayHi.counter++;
}
sayHi.counter = 0; // 初期値

sayHi(); // Hi
sayHi(); // Hi
alert( `Called ${sayHi.counter} times` ); // 2度呼ばれた
```
例えば上記のように実行回数をカウントできる

- 名前付き関数式(Named Function Expression)
名前付き関数式、略して NFE は名前を持つ関数式の用語

```javascript
let sayHi = function func(who) {
  alert(`Hello, ${who}`);
};
sayHi("John"); // Hello, John
```
whoに代入できる
funcの箇所は再帰のように使いたいので自分で名前を付けている

### 感想
関数オブジェクトには、使用可能なプロパティが少ししかないので、使用できるプロパティを覚えておくと便利だと思った。



## 6.7 "new Function" 構文
### 知らない単語
- 無し

### 学んだこと
- new Functionを使ったコード
```javascript
let sum = new Function('a', 'b', 'return a + b');

alert( sum(1, 2) ); // 3
```
このように「return a + b」が省略されて出力されるが、一瞬でこれを判断するのは難しい上に、見辛いのであまり使わない

- クロージャ

通常の関数は作成された場所からレキシカル環境を参照するので、どこで生成されたかを覚えている
だが、new Function を使用して作られた関数の場合、現在のレキシカル環境ではなく、グローバルのレキシカル環境を参照する
要するにnew Functionだとその場で定義していなければ使えないという事
```javascript
function getFunc() {
  let value = "test";

  let func = new Function('alert(value)');

  return func;
}
getFunc()(); // error: value は未定義
```
上記のコードは通常のfunctionであればエラーにならない

### 感想
new Functionと通常のfunctionを上手く使い分けていきたい。

## 6.8 スケジューリング: setTimeout と setInterval
### 知らない単語
- 無し

### 学んだこと
ある時点で実行するようにしたいことがある時に使用する2つのメソッド

setTimeout()は指定時間経過後、一度だけ関数を実行する
setInterval()は各実行の間は指定した間隔で、定期的に関数を実行する

- setTimeout()
```javascript
let timerId = setTimeout(func|code, delay[, arg1, arg2...])
```
func|code
関数もしくは実行するコードの文字列
通常は関数です、コードの文字列も渡すことができるが、推奨されない

delay
実行前の遅延時間で、ミリ秒単位 (1000 ms = 1 秒).

arg1, arg2…
関数の引数

- clearTimeout を使ったキャンセル

clearTimeoutはsetTimeoutをキャンセルできる
書き方は
```javascript
let timerId = setTimeout(...);
clearTimeout(timerId);
```

- setInterval
setInterval は setTimeout と同じ構文を持ち、全ての引数が同じ意味だが、 setTimeout とは異なり、関数を1回ではなく定期的に与えられた時間間隔で実行する

### 感想
自分でタイマーのアプリを作った時に使用したメソッドだったので、さらに詳しく理解できた。


## 6.9 スケジューリング: デコレータと転送, call/apply
### 知らない単語
- デコレータとは
    - すでにある関数に処理の追加や変更を行う為の機能

- キャッシュとは
    - すでにある関数に処理の追加や変更を行う為の機能

### 学んだこと
- コンテキストのために、“func.call” を利用する

ラッパーは行でオリジナル関数をfunc(x)として呼び出すため、他の行からthisなどを呼び出した場合、関数は this = undefined となる

ラッパーは呼び出しを元のメソッドに渡すが、コンテキスト this は使用しないのでエラーになる
これを直すために this で関数を呼び出すことができる、メソッド func.call(context, …args) がある

コードは
```javascript
func.call(context, arg1, arg2, ...)
```
などと書く

「func.apply」も「func.call」とほぼ同等の意味だが、callは引数でスプレッド演算子を使う必要がある

### 感想
「func.apply」と「func.call」をどのような場面で使ったら良いかを的確に判断していきたい。


## 6.10 関数バインディング
### 知らない単語
- バリアントとは
    - プログラムの実行条件を設定した値を管理するオブジェクトの事

### 学んだこと
- “this” を失う

ここで解説されている事をまとめると
書き方によってthisはかわるのでエラーになる
解決策としてはfunctionを用いてthisを明確にする必要がある
もう1つはthis を固定できる組み込みメソッドbind使用する
bindの基本コードは
```javascript
let boundFunc = func.bind(context);
```
となるが、functionをfuncにbindしている

前にも書いたが、bindは紐付ける、関連付けるという意味

つまり、オブジェクトメソッドで this を固定するために bind を適用し、例えばsetTimeout()など、どこかに渡せるようにする

### 感想
bindはコンテキストを省略して引数だけ指定する事はできないので、書き方だけでなく、ルールもしっかり覚えておく。


## 6.11 アロー関数ふたたび
### 知らない単語
- 無し

### 学んだこと
アロー関数は小さなものを書くための単純な簡略化では無い
特徴としては
例えば
arr.forEach(func)のfunc は forEach によって、すべての配列項目に対して実行される
setTimeout(func)のfunc は組み込みのスケジューラにより実行される
など関数を作成して渡すのがJavaScriptの根幹である

- アロー関数は “this” を持っていません

this がアクセスされた場合、外側から取られる
例えば下記のコードでは
```javascript
let group = {
  title: "Our Group",
  students: ["John", "Pete", "Alice"],

  showList() {
    this.students.forEach(
      student => alert(this.title + ': ' + student)
    );
  }
};
group.showList();
```
forEach では、アロー関数が使われているので、その中の this.title は外部のshowList()と同じである

- アロー関数は “arguments” を持ちません

アロー関数は argumentsも持っていないので、例えばアロー関数を使った下記のコードがあるとして
```javascript
function defer(f, ms) {
  return function() {
    setTimeout(() => f.apply(this, arguments), ms)
  };
}

function sayHi(who) {
  alert('Hello, ' + who);
}

let sayHiDeferred = defer(sayHi, 2000);
sayHiDeferred("John"); // Hello, John 2秒後
```

アロー関数を使わない場合は下記のようになる

```javascript
function defer(f, ms) {
  return function(...args) {
    let ctx = this;
    setTimeout(function() {
      return f.apply(ctx, args);
    }, ms);
  };
}
```

このようなコードの場合、アロー関数にはthisもargumentsも無いので上のように書くしか無いが、通常は下の方が見やすいコードなので下の書き方で書かれる
人によっては上の方が書きやすい人もいるかもしれない


### 感想
アロー関数はthisもargumentsも持たず、newで呼び出すこともできないが、独自のコンテキストを持たないので、それが望まれる場面では重宝されるのだと思う。

# 最後に
func.callは引数でスプレッド演算子を使う必要があるなど、サイトで書いてある内容を読み取るのがかなり難しくなっていたりするのでアドベントカレンダーが終わってから理解を深めるために復習していきたいと思う。今後もさらに難しくなると自分だけではわからない所も多くなりそうである。