javascriptをudemyで学んでみた（関数とオブジェクト編）

udemyの[ガチで学びたい人のためのJavascriptメカニズム](https://www.udemy.com/course/javascript-essence )という講座を受講しまして、
自分の言葉でまとめたいなと思ったのでこの記事を書きました。

最近エンジニアになったばかりですので暖かい目で見ていただければと思います

# jsを学ぶ理由
シンプルに「Reactを使ってみたいなー」と思ったからです
しかし、どうやらReactを使うにはjsの基礎がわかっていないと話にならんとのことだったので、とりあえず素のjsしっかり学ぼうと思いました。教材に触れる前の自分のレベル感としては「プログラミングの基礎はある程度わかる」「DOM操作も何となくわかる」「非同期処理はJQueryを使ってならやったことある」です

ちなみに知らなかった用語としては「スコープ」「クロージャー」「モジュール」などです
（スコープに関しては今まで意識して使ったことがないというのが正しいかもです）

# Reactに必要な知識
[こちら](https://dev-k.hatenablog.com/entry/react-top_skill-_ist-of-javascript )の記事を参考してに学ぶ項目を決めました
キーワードをざっと羅列するとスコープ、モジュール、非同期処理、スプレッド演算子、三項演算子、ES6クラス etc...

# この教材のレベル感
正直なかなか難しいです
メモリレベルでのお話や、ブラックボックスになっている処理をあえて解き明かしたりと、
とても深い領域まで解説されています

一応講座自体は全て目は通しているのですが、とても難しくて重要度が高くないと判断した箇所に関しては、後で出てきた際に **「あ、なんか見たことある〜」** 程度の理解で十分かなと思っています

ですので、「そこまではいらんかな〜」と感じたところはバリバリ端折るので許してください

# jsにおける関数の挙動

### 関数に渡す引数が足りない場合
まずは以下のコードを見てください
この場合の実行結果はどうなるでしょうか？？

```javascript:qiita.js
function fn(a, b) {
    console.log(a, b);
}

fn(1); //引数が一つしかない
```

他の言語なら、エラーを吐いて怒られそうですがjsの場合はこのようになります

```javascript:qiita.js
fn(1) // 1 undefined
```

なんと、、、エラーにならずundefinedが設定されます

### 同じ名前の関数が宣言されていた場合

それじゃあ関数fnが二つ定義されている場合はどうなるでしょうか？

```javascript:qiita.js
function fn(a, b) {
    console.log(a, b);
}

function fn(a, b) {
    console.log(a, b);
}

fn(1,2);
```
頼むから、エラー吐いてくれ！
```javascript:qiita.js
fn(1,2);//1,2
```

結果はなーんと、一番下に書いた関数がそのまま実行されちゃうんですね〜
ちなみに引数の数が異なっていたとしても、jsでは別の関数として認識はされません

ではどうすればいいかというと、**「関数式」**　を使うことでこの問題は解決できます
以下のコードを見てください
```javascript:qiita.js
const fn = function(a, b) {
    console.log(a, b);
}

function fn(a, b) {
    console.log(a, b);
}

fn(1,2); // エラー！　
```
無事エラーを吐きました！
関数式を使うことで同様の名前を持つ関数が再宣言ができなくなります

**ひと言メモ** 
普通に関数を定義する場合と関数式を使う場合の大きな違いは、**関数の再宣言を防ぐこと**でそこまで大きな違いはないです
ですが、のちに出てくる**アロー関数**は普通に関数を定義する際と大きく異なる点があるので注意が必要です

### argumentsとは？
argumentsとは **「関数コンテキストによって自動で生成されるオブジェクト」** です
簡単にいうと **「関数スコープ内で使える便利なやつ」**　という認識で間違っていないと思います

argumentsは**関数に渡された実引数**を保持することができます

以下のコードで確認してみましょう！
```javascript:qiita.js
function fn() {
	const a = arguments[0];
	const b = arguments[1];
	console.log(arguments);
	console.log(a,b);
}

fn(1,2);
```

fnに引数1と2が渡っているのにも関わらず、仮引数が空欄のままです
ですが、jsは指定した実引数をargumentsの内部に自動で格納してくれます

これは渡される引数の数が決まってない場合に使われるそうです
とても便利ですね


 **しかし！！！！！**


実は最近だとargumentsはあまり使われなくなっています

その理由は大きく2つあり
・**argumentsは配列と比べて扱いずらい** 
・**ES6でRestパラメーターというものが導入された**

からです

Restパラメータは不特定多数の引数を**配列**として格納してくれるので、
わざわざ扱いずらいargumentsを採用しなくなったという背景があります

では、Restパラメーターを使ってみましょう！

```javascript:qiita.js
function fn(...args) {
    console.log(args[0]);
}

fn(1,2); //１←(配列として値が格納される！)
```

こんな感じ、仮引数の前に...を取るのが特徴です

**ひと言メモ** 
ちなみに同様に...を取るスプレッド演算子というものがあるのですが、これとは処理の内容が全然違うので注意してください
スプレッド演算子は渡された配列やオブジェクトを展開する構文です

# コールバック関数 
コールバック関数とは他の関数に**引数として**渡せる関数のことを言います

以下のコードを見てください

```javascript:qiita.js
function hello(name) {
    console.log('hello ' + name);
}

function fn(cb) {
    cb();
}

fn(hello);
```

関数fnの引数に関数helloが渡っているのが確認できますね

ただ、この講座を受けていて自分が疑問に感じたのは **「結局コールバック関数を使うメリットてなんだ？」** ということです
わざわざ関数の引数に関数を渡してその中で実行されるってかなりの手間じゃないかと思いました

講座の中では **「汎用性が高まる」** という説明がありましたが 「なんのこっちゃい」と思ってしまい、いまいちピンと来ませんでした

そこで色々わかりやすそうな記事や動画を探した結果、自分はこちらの動画でコールバック関数のメリットを理解しました！

https://youtu.be/sFA-OI1r5fs

震えるほどわかりやすいので、スッキリしたい方はどうぞご覧ください〜
７分ほどなので、そこそこ太めのパスタ茹で上がる頃には終わってます

# this
thisとは**呼び出し元のオブジェクトへの参照を保持する**キーワードです
オブジェクト指向プログラミングをしたことある方なら、一度は使ったことがあると思います

こちらも、argumentsと同様に関数コンテキスト内で自動生成されるオブジェクトの一つになります
特筆すべき特徴が２つあり

・実行コンテキストによって参照先が変わる
・**呼びだし元のオブジェクト**の参照を保持する

まあ、日本語だとなんのことかいまいちピンとこないので、とりあえずコード見て確認しましょう

```javascript:qiita.js
const person = {
    name: 'Tom',
    hello: function() {
        console.log('Hello ' + this.name);
    }
}
person.hello(); //Hello Tom
```

上記の例は、**オブジェクトのメソッド**として関数が実行された場合です
答えはHello Tomになります

thisは呼び出し元のオブジェクトを参照するので、この場合はhello関数のレキシカルスコープであるpersonオブジェクト内のnameプロパティの値を取得します

そして次にこちらの例を見てください
簡単に説明すると、hello関数を定数に一度代入してそれを実行しているだけです
```javascript:qiita.js
window.name = 'John';

const person = {
    name: 'Tom',
    hello: function() {
        console.log('Hello ' + this.name);
    }
}

const ref = person.hello;
ref();// Hello John
```

結果はなーんと、関数をコピーしただけなのにグローバルスコープの値を取得してしまってますね〜

そう、関数として実行される場合 **thisはグローバルオブジェクトを参照する**という特徴を持ってしまうのです

まとめるとこうです
・**オブジェクトのメソッドとして**実行される場合、thisは**呼び出し元のオブジェクト**を参照する
・**関数として**実行される場合、thisは**グローバルオブジェクト**を参照する

### なぜこのような違いが生まれるのか
**内部の処理まで興味ない方は飛ばしても大丈夫です！**

こちらは、メモリ空間内の参照の関係を図式化したものです
![qiita.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1096415/c9733680-207e-8e9b-b660-757cf1c718b0.png)

メソッドをコピーすると、helloが参照している関数の参照がコピーされます(赤矢印)
そして、コピーされた関数に対してrefから参照が通ることになります

つまり、refを実行する際は **personオブジェクトを経由していない** のでthisはグローバルオブジェクトへの参照を保持することになってしまう
その結果として、windowオブジェクトのnameの値を取得することになります

# bindとcall apply
先ほどのセクションでthisの挙動は理解できたと思いますが、**「いや〜関数をコピーしたとしてもオブジェクトのプロパティ使いたいな〜、グローバルスコープに同様のプロパティを追加するのやだな〜、なんかいい方法ないかな〜」**　と思う方がいるかもしれません

**いい方法あるんですよ！！！**
thisの参照先や引数を束縛する方法が

そのキーワードが**bind、call、apply**です

まずはbindの動きを確認していきましょう!

ちなみに、下記の例ではコールバック関数を使用していますが、
コールバック関数も関数をコピーして実行した際とthisの参照先は変わらないので問題ありません

```javascript:qiita.js
window.name = 'John';

const person = {
    name: 'Tom',
    hello: function() {
        console.log('Hello ' + this.name);
    }
}

const helloTom = person.hello.bind(person)

function fn(ref) {
    ref();
}

fn(helloTom);//Hello Tom
```

第一引数にthisが参照するオブジェクトを指定するだけです
これだけで、実行結果がHello Tomとなります
簡単ですね

ちなみに、**bindは第２引数に値を指定してそれを束縛する**こともできます
もちろんcallとapplyも同様のことが可能です

### call、applyとは何が違うの？
bindとcall、applyの違いは**すぐに実行されるかどうか**だけです

先ほどのbindを用いたコードでは、一度helloTomという定数に格納してそれを実行していました
callとapplyは使うとすぐに関数が実行されます

ちなみに、callとapplyの違いはというと**束縛する引数が配列かそうでないか**という違いなだけです
callはbindと同様に値を指定し、applyは配列を指定します

では、コードを見て確認しましょう
```javascript:qiita.js
function a(name, name1) {
    console.log('hello ' + name + ' ' + name1);
}

const tim = {name: 'Tim'};

a.call(tim, 'Tim', 'Bob'); //hello Tim
a.apply(tim, ['Tim', 'Bob']); //hello Tim
```

確かに、callは文字列が渡されておりapplyには配列が渡されています
結果はどちらも同じですね

# アロー関数
アロー関数とはES6で導入された、**無名関数を記述しやすくした省略記法**です

アロー関数に関してはさまざまな書き方があるので、とりあえずそれらをみてみましょう

```javascript:qiita.js
//関数宣言
function a(name) {
    return 'hello ' + name;
}

console.log(a('Tom')); //hello Tom

//関数式
const b = function(name){
    return 'hello ' + name;
}

console.log(b('Tom')); //hello Tom


//引数を持つアロー関数その１
const c = (name) => {
    return 'hello ' + name;
}

console.log(c('Tom')); //hello Tom


//引数を持つアロー関数その2
const d = name => 'hello ' + name;

console.log(d('Tom')); //hello Tom


//引数を持たないアロー関数その3
const e = () => 'hello ';

console.log(e()); //hello

//引数を持たないアロー関数その4
const f = _ => 'hello ';

console.log(f()); //hello
```

アロー関数と一口に言っても、いろいろな書き方があるんだないうことがわかりますね

ただ、初めにこれをみた自分の率直な意見は **「うわ〜可読性下がりそう、、、」**　でした
みんなjsに精通しているような開発現場でないかぎり、アロー関数を省略しまくるのはちょっとどうなんだと個人的には思ってしまいました

それとも、現場レベルになるとこの省略は案外普通レベルなことなのでしょうか？？

### アロー関数の注意点
アロー関数が普通の関数宣言と異なる点があり、それは**アロー関数はthisの値がセットされないこと**です

```javascript:qiita.js
window.name = 'John';

const person = {
    name: 'Tom',
    hello: () => {
        console.log('Hello ' + this.name);
    }
}
person.hello(); //Hello John

```

上記のコードは、普通に考えるとメソッドを実行しているのでpersonオブジェクトの'Tom'を参照すると考えると思います

しかし、アロー関数はそのコンテキスト内ではthisを取らないのでスコープチェーンをたどり上のグローバルコンテキストを参照することになリます
従って、windowオブジェクトのnameプロパティの値が参照されるという流れになります

つまり、**「アロー関数ではレキシカルスコープのthisが参照される」** ということですね！

# 終わりに
今回は関数とオブジェクト編として学習したことをまとめました
次回は非同期処理についてまとめたいと思います

最後に、クロージャーとアロー関数を組み合わせたコードを見て終わりにしましょう
```javascript:qiita.js
//通常の状態
function addNumberFactory(num) {
    function addNumber(value) {
        return num + value;
    }
    return addNumber;
}

//アロー関数で省略した状態
const addNumberFactory = num => value => num + value;
```
お疲れ様でした〜