こちらの記事は以下の書籍を参考に執筆しました
入門JavaScriptプログラミング
以下のコードはvar
、let
それぞれにのイテレータにより宣言されたfor文であるが、結果が異なる。
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)
}
#let
のスコープの仕組み
let
はブロックスコープである。
スコープは変数が宣言されたブロックまたはサブブロックの内側だけである。
これにより、ブロックの外側で使用されたためバグが紛れ込むということが無くなる。
また、以下のようにブロックにより変数をプライベートに保つことができる。
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を参照しているためエラー
##for文での例外
for文で宣言されたlet変数のスコープはforループのブロックとなる。
for (let i = 0; i < 5; i++) {
console.log(i);//iのスコープはforブロック
}
console.log(i);//エラー
##letが推奨される理由
var
は関数スコープ。
そのため、変数には宣言された変数のどこからでもアクセスできる。
(function(){
if (true){
var foo='bar';
}
console.log(foo);//ifブロックの外からアクセス可能
}());
以下はvar
の典型的な例である。
<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>
どのli
タグをクリックしてもfive:5
と表示される。
イベントリスナはfor文が完了するまで呼び出されず、i``li.textontent
が表示される頃にはそれぞれ5とfiveが設定されているためである。
##変数のシャドーイング
以下のコードを実行すると2つ目の"scope"
は実行されない
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);
}
}
2つ目のforループが完了した際にはiは7になっているため。
この場合両方letで宣言してあげると解決できるが、これは変数のシャドーイングと呼ばれ、一般的には悪い作法だ。
内側のループには別の変数名を定義すべき。