【javascript】letによる変数宣言

こちらの記事は以下の書籍を参考に執筆しました
 [入門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)

以下のコードは`var`、`let`それぞれにのイテレータにより宣言されたfor文であるが、結果が異なる。

```javascript
for (var i = 0; i < 5; i++) {
  setTimeout(function(){console.log("var:"+i)},1)
}

for (let n = 0; n < 5; n++) {
  setTimeout(function(){console.log("let:"+n)},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)

#`let`のスコープの仕組み
`let`はブロックスコープである。
スコープは変数が宣言されたブロックまたはサブブロックの内側だけである。

これにより、ブロックの外側で使用されたためバグが紛れ込むということが無くなる。
また、以下のようにブロックにより変数をプライベートに保つことができる。

```javascript
let read, write; 
{
  let data = {};//dataは実質的にプライベート変数

  write = function(key, val) {
    data[key] = val;
  }
  read = function(key) {
    return data[key];
  }
}

write(
  'message',
  'welcome to ES6'
);
read('message');
console.log(data);//ブロックの外でdataを参照しているためエラー
```

>出典：[入門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)


##for文での例外
for文で宣言されたlet変数のスコープはforループのブロックとなる。

```javascript
for (let i = 0; i < 5; i++) {
  console.log(i);//iのスコープはforブロック
}

console.log(i);//エラー
```
>出典：[入門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が推奨される理由
`var`は**関数スコープ**。
そのため、変数には宣言された変数のどこからでもアクセスできる。

```javascript
(function(){
  if (true){
    var foo='bar';
  }
  console.log(foo);//ifブロックの外からアクセス可能
}());
````

>出典：[入門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)


以下は`var`の典型的な例である。

```html
<ui>
  <li>one</li>
  <li>two</li>
  <li>three</li>
  <li>four</li>
  <li>five</li>
</ui>
<script type="text/javascript">
  var items = document.querySelectorAll('li');
  for (var i = 0; i < 5; i++) {
    var li = items[i];
    li.addEventListener('click', function() {
      alert(li.textContent + ':' + i);
    })
  }
</script>
```
>出典：[入門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)



どの`li`タグをクリックしても`five:5`と表示される。

イベントリスナはfor文が完了するまで呼び出されず、`i``li.textontent`が表示される頃にはそれぞれ5とfiveが設定されているためである。


##変数のシャドーイング
以下のコードを実行すると2つ目の`"scope"`は実行されない

```javascript
var words = ["function", "scope"];
for (var i = 0; i < words.length; i++) {
  var word = words[i];
  for (var i = 0; i < word.length; i++) {
    var char = word[i];
    console.log('char', i, char);
  }
}
```

>出典：[入門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)


2つ目のforループが完了した際にはiは7になっているため。
この場合両方letで宣言してあげると解決できるが、これは**変数のシャドーイング**と呼ばれ、一般的には悪い作法だ。
内側のループには別の変数名を定義すべき。