let だと for 文+関数作成時の問題が起きない
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ let + for 文にすると関数を内部で作ってもループ変数は各回で固定なので大丈夫です
◆ JavaScript では while で for と全く同じ動きするものは作れない?
◆ JavaScript では while で for と全く同じ動きするものは作れない?
for 文のループで関数を登録すると 関数を実行したときはループ変数が最後になってるので思い通りに動かないというのは有名なハマりどころですよね
let のブロックスコープだとこの問題は起きない?
ループ変数はインクリメントしたりするのでループの各ブロックで共有しているから無理?
と気になったので調べてみます
var
let
ちゃんと 0 1 2 になってます
ということはループ変数はループの各ブロックで別物?
i++ の上にもう一つスコープがあるのは i++ のところに for の内側で宣言したものを使えないからです
x is not defined
って言われますよね
この while だと while の外側に let があるので 全ループでループ変数は共通です
なので 関数を登録しても
for 文のように 0 1 2 と表示させるようにするには let i の宣言を while の内側に入れないとだめです
ですが そうすると インクリメントできません
余計な変数が見えますが こういうことをすればできます
_counter の変数をなくせないので スコープも含めて全く同じ動きを while でさせるのは不可能そうです
変更しなければ const でも問題なく使えます
もしかしてこの特殊な動きは JavaScript で楽できるような特殊仕様であって 一般のブロックスコープではない?と思って別の言語で試してみようと思います
スクリプト言語でブロックスコープって聞かないので 静的型付け言語で JavaScript っぽさのある c# で試してみます
3 ですね
やっぱりこの動きが自然なんですよ
JavaScript の let は特殊!!
let のブロックスコープだとこの問題は起きない?
ループ変数はインクリメントしたりするのでループの各ブロックで共有しているから無理?
と気になったので調べてみます
let のスコープ
var
var fns = []
for(var i=0;i<3;i++){
fns[i] = function(){console.log(i)}
}
fns.forEach(e => e())
for(var i=0;i<3;i++){
fns[i] = function(){console.log(i)}
}
fns.forEach(e => e())
3
3
3
3
3
let
var fns = []
for(let i=0;i<3;i++){
fns[i] = function(){console.log(i)}
}
fns.forEach(e => e())
for(let i=0;i<3;i++){
fns[i] = function(){console.log(i)}
}
fns.forEach(e => e())
0
1
2
1
2
ちゃんと 0 1 2 になってます
ということはループ変数はループの各ブロックで別物?
while に直すと
for は while にするとこうなるんだと思ってましたfor(var i=0;i<3;i++){
// for の中身
}
↓// for の中身
}
{
let i = 0
while(i < 3){
{
// for の中身
}
i++
}
}
let i = 0
while(i < 3){
{
// for の中身
}
i++
}
}
i++ の上にもう一つスコープがあるのは i++ のところに for の内側で宣言したものを使えないからです
for(var i=0;i<10;i+=x){
let x = i * 2
}
これを実行するとlet x = i * 2
}
x is not defined
って言われますよね
この while だと while の外側に let があるので 全ループでループ変数は共通です
なので 関数を登録しても
var fns = []
{
let i = 0
while(i < 3){
{
fns[i] = function(){console.log(i)}
}
i++
}
}
fns.forEach(e => e())
{
let i = 0
while(i < 3){
{
fns[i] = function(){console.log(i)}
}
i++
}
}
fns.forEach(e => e())
3
3
3
こうなります3
3
for 文のように 0 1 2 と表示させるようにするには let i の宣言を while の内側に入れないとだめです
ですが そうすると インクリメントできません
余計な変数が見えますが こういうことをすればできます
var fns = []
{
let _counter = 0
while(_counter < 3){
let i = _counter
{
fns[i] = function(){console.log(i)}
}
_counter++
}
}
fns.forEach(e => e())
{
let _counter = 0
while(_counter < 3){
let i = _counter
{
fns[i] = function(){console.log(i)}
}
_counter++
}
}
fns.forEach(e => e())
0
1
2
1
2
_counter の変数をなくせないので スコープも含めて全く同じ動きを while でさせるのは不可能そうです
関数だと
なんとなく関数で for を作ってみましたvar fns = []
let i = 0
;[i] = function recur(i){
if(i<3){
{
fns[i] = function(){console.log(i)}
}
return recur(i + 1)
}else{
return [i]
}
}(i)
fns.forEach(e => e())
いろいろと柔軟性にかけてそうですlet i = 0
;[i] = function recur(i){
if(i<3){
{
fns[i] = function(){console.log(i)}
}
return recur(i + 1)
}else{
return [i]
}
}(i)
fns.forEach(e => e())
const
ついでなので const でやってみると 当たり前ですが for の3 つ目のブロックのインクリメント部分で代入できないエラーです変更しなければ const でも問題なく使えます
ところで
他の言語では while で for と同じものができたような記憶がありますもしかしてこの特殊な動きは JavaScript で楽できるような特殊仕様であって 一般のブロックスコープではない?と思って別の言語で試してみようと思います
スクリプト言語でブロックスコープって聞かないので 静的型付け言語で JavaScript っぽさのある c# で試してみます
var list = new List<Action> { };
for(var i = 0; i < 3; i++)
{
list.Add(() => Console.WriteLine(i));
}
list.ForEach(f => f());
for(var i = 0; i < 3; i++)
{
list.Add(() => Console.WriteLine(i));
}
list.ForEach(f => f());
3
3
3
3
3
3 ですね
やっぱりこの動きが自然なんですよ
JavaScript の let は特殊!!