varとletとクロージャーとブロックスコープと・・・

varとletとクロージャーとブロックスコープと・・・:


はじめに

ES7,ES8はもちろん、ES6でもいまだに「へぇ〜」と思うことが出てきて

自分の理解の「狭さ」や「浅さ」に驚くが、めげずに備忘録をつけていこうと思う

それに公の場にさらしておけば、親切な人が間違いを指摘してくださることもあるし


変数参照の勘違い問題を解決するlet

以下は言い古され感のある初心者がやりがちな有名なサンプルですね

const operations = []    
for (var i = 0; i < 5; i++) { 
  operations.push(() => { 
    console.log(i) 
  }) 
} 
for (const operation of operations) { 
  operation()                          // 5 5 5 5 5 
} 
昔の解決策としては以下のようになります

const operations = [] 
for (var i = 0; i < 5; i++) { 
  (i => { 
    operations.push(() => { 
      console.log(i) 
    }) 
  })(i) 
} 
for (const operation of operations) { 
  operation()                         // 0 1 2 3 4 
} 
ES6以降では以下のようにすれば問題を回避できる

const operations = []    
for (let i = 0; i < 5; i++) { 
  operations.push(() => { 
    console.log(i) 
  }) 
} 
 
for (const operation of operations) { 
  operation()                         // 0 1 2 3 4 
} 
単に var を let にしただけ。

やりがちな問題としての原因が、直感的に間違いのコードを書いてしまうわけだから

そのとおりの記述でvarをletにするだけで修正できるのはいいことだと思います


理解の確認とポイント

たとえばブロックスコープの説明を見ただけで、このことを連想できるのは難しいと思う。

ということはその他のことについての理解も表面的なことにとどまってしまってる

可能性があるということで、1つ1つもう少し掘り下げていかないとな〜とおもいます

で、少しだけブロックスコープについて確認した内容をクロージャといっしょにまとめます

間違いなどあればご指摘いただければと思います


クロージャー

内側の関数は外側の関数のスコープへの参照を持っているので、

外側の関数のプライベート変数にアクセスでき、また、

外側の関数は内側の関数から参照されている変数の参照カウントをゼロにせずに

GCに持ってかれないように保持しているのがクロージャーの仕組み

ポイントは内側の関数が持ってるのは外側の関数の変数の参照であり値ではないこと


ブロックスコープ

いくつか見てみましたが一番スッと理解出来た文が以下でした。

上記のfor文の変数 i について、サンプルとともに掲載されてたものです

This works because on every loop iteration i is created as a new variable

each time, and every function added to the operations array gets its own
copy of i.                     参照元へ
要は昔の解決策のIIFEの引数の役割をそのまましてくれてるわけですね。

この文は、この件に限らず、ブロックスコープの少し深めの理解につながったかなと思います。

コメント