【javascript】変数の巻き上げ

こちらの記事は以下の書籍を参考に執筆しました

- [入門JavaScriptプログラミング](https://www.amazon.co.jp/%E5%85%A5%E9%96%80JavaScript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-JD-Isaacks/dp/479815864X/ref=sr_1_3?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1F76CKY9CIJ66&dchild=1&keywords=%E5%85%A5%E9%96%80javascript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0&qid=1595991879&s=books&sprefix=%E5%85%A5%E9%96%80javascript%2Cstripbooks%2C-1&sr=1-3)
プロパティ名と変数名が同じになる場合はこのように簡潔にできる。



##letによる変数の巻き上げ
**巻き上げ**とは、変数が宣言されたスコープの内側では、変数がスコープ全体(letならブロック全体,varなら関数全体)を消費する振る舞いのこと。

**...そう言われてもよくわからない。**
まずは例を見てみる。

```javascript
if (condition) {
  //----- myDataのスコープはここから -----
  doSomePrework();
  /*その他のコード*/
  let myData = getData();
  /*その他のコード*/
  doSomePostwork();
  //----- ここまで -----
}
```
>出典：[入門JavaScriptプログラミング](https://www.amazon.co.jp/%E5%85%A5%E9%96%80JavaScript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-JD-Isaacks/dp/479815864X/ref=sr_1_3?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1F76CKY9CIJ66&dchild=1&keywords=%E5%85%A5%E9%96%80javascript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0&qid=1595991879&s=books&sprefix=%E5%85%A5%E9%96%80javascript%2Cstripbooks%2C-1&sr=1-3)



上記の例では`myData`変数はif文のブロックスコープ内にあり、
まだ宣言されていなくても**アクセス**できる。
` //----- myDataのスコープはここから -----`と` //----- ここまで -----`の間のどこに`myData`を宣言してもどこからでもアクセスできるということ。

次の例は少々難しい。

```javascript
//----- 外側のmyDataのスコープはここから -----
let myData = getDefaultData();

if (condition) {
  //----- myDataのスコープはここから -----
  doSomePrework(myData);
  /*その他のコード*/
  let myData = getData();
  /*その他のコード*/
  doSomePostwork();
}
//----- ドト側のmyDataのスコープはここまで -----
```
>出典：[入門JavaScriptプログラミング](https://www.amazon.co.jp/%E5%85%A5%E9%96%80JavaScript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-JD-Isaacks/dp/479815864X/ref=sr_1_3?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1F76CKY9CIJ66&dchild=1&keywords=%E5%85%A5%E9%96%80javascript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0&qid=1595991879&s=books&sprefix=%E5%85%A5%E9%96%80javascript%2Cstripbooks%2C-1&sr=1-3)




上記の場合`myData`は**2つ**存在する。
ややこしいのは`doSomePrework`関数が呼び出された時点で引数の`myData`は外側の`myData`であるというのは間違であるということだ。
つまり、
内部の`myData`はスコープ全体(ifブロック全体)を消費しているため、`doSomePrework`関数に入るのはこの内側の`myData`だ。
このように宣言される前でも変数を巻き上げて使用できてしまうのが**変数巻き上げ**である。

#letで巻き上げられる変数へ宣言前にアクセスすると？
letは変数をブロックの先頭へ巻き上げ、varは関数の先頭へ巻き上げる。
letで宣言された変数が宣言される前にスコープ内でアクセスされた場合、参照エラー(**ReferenceError**)となる。
varではスコープ内であれば宣言される前であってもアクセスできるが、値は常に**未定義**となる。

```javascript
console.log(foo) //参照エラー
let foo=2;
```
>出典：[入門JavaScriptプログラミング](https://www.amazon.co.jp/%E5%85%A5%E9%96%80JavaScript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-JD-Isaacks/dp/479815864X/ref=sr_1_3?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1F76CKY9CIJ66&dchild=1&keywords=%E5%85%A5%E9%96%80javascript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0&qid=1595991879&s=books&sprefix=%E5%85%A5%E9%96%80javascript%2Cstripbooks%2C-1&sr=1-3)



以下のコードの結果はどうなるだろうか

```javascript
let num = 0;

function getNum() {
  if (!num) {
    let num = 1;
  }
  return num;
}

console.log(getNum());  //結果は??
```
>出典：[入門JavaScriptプログラミング](https://www.amazon.co.jp/%E5%85%A5%E9%96%80JavaScript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-JD-Isaacks/dp/479815864X/ref=sr_1_3?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1F76CKY9CIJ66&dchild=1&keywords=%E5%85%A5%E9%96%80javascript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0&qid=1595991879&s=books&sprefix=%E5%85%A5%E9%96%80javascript%2Cstripbooks%2C-1&sr=1-3)



答えは1ではない。

解説すると、

```javascript
let num = 0;

function getNum() {
  if (!num) {
    //----- 内部のnumスコープはここから -----
    let num = 1;
    //----- 内部のnumスコープはここまで -----
  }
  return num;  //新しい let 変数はスコープを外れたため、この変数は0のまま 
}

console.log(getNum());
```

>出典：[入門JavaScriptプログラミング](https://www.amazon.co.jp/%E5%85%A5%E9%96%80JavaScript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-JD-Isaacks/dp/479815864X/ref=sr_1_3?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1F76CKY9CIJ66&dchild=1&keywords=%E5%85%A5%E9%96%80javascript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0&qid=1595991879&s=books&sprefix=%E5%85%A5%E9%96%80javascript%2Cstripbooks%2C-1&sr=1-3)


つまり、は`if`文内で宣言された`num`そのスコープ内でしか有効でないため`getNum`関数の`return`される`num`は外側のものであるということだ。

これを解決するにはnumに1を代入する時にletを取ればいい。

```javascript
let num = 0;

function getNum() {
  if (!num) {
    //letをとる。
    num = 1;
  }
  return num;
}

console.log(getNum());  //1
```

>出典：[入門JavaScriptプログラミング](https://www.amazon.co.jp/%E5%85%A5%E9%96%80JavaScript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-JD-Isaacks/dp/479815864X/ref=sr_1_3?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1F76CKY9CIJ66&dchild=1&keywords=%E5%85%A5%E9%96%80javascript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0&qid=1595991879&s=books&sprefix=%E5%85%A5%E9%96%80javascript%2Cstripbooks%2C-1&sr=1-3)