JavaScript/関数/クロージャ

JavaScript/関数/クロージャ

クロージャとは

ようするにあれ

var と let での変数の持ち込み方が違う

var で宣言された変数と let で宣言された変数はクロージャへの値の持ち込まれ方が違う。

これは let がブロックスコープであるという仕様に起因する。

var は昔からある通りの挙動で、var で作られた変数の参照を持ち込むことになる。 つまり持ち込んだ関数が実行された時の var の状態がそのまま反映される。

let はちょっと違っていて、その関数が作られたタイミングの let の内容で確定されて持ち込まれる(ように見える)。

ここの2つのコードは挙動が違う

for(var i = 0; i < 10; i++){
    let tag = document.createElement("button");
    tag.innerHTML = i;
    tag.addEventListener("click", function(){
        console.log(i);
    });
    hoge.appendChild(tag);
}
for(let i = 0; i < 10; i++){
    let tag = document.createElement("button");
    tag.innerHTML = i;
    tag.addEventListener("click", function(){
        console.log(i);
    });
    hoge.appendChild(tag);
}

var バージョンではどの生成されたどのボタンを押してもコンソールには 11 と表示されるだろう。 しかし、let バージョンではボタンに設定した数字と同じ数字が出る。

var 側がボタンがすべて生成し終わった後の i をすべてのボタンに対して反映しているのに対して、 let 側は関数作成時の i で確定している(ように見える)。

これは let が let する度に新規作成される変数だからということである。 状況として let はブロック内で宣言され、ブロック内で生成されたモノが代入される可能性が高い。 なので、作成時で確定とみなしてもほぼ間違いは無い。

このことから、ブロック外部で let を生成すると var と同じ挙動になる。 これは var のスコープが勝手に function 全体に拡大してしまうことに問題がある。

var をブロックスコープ的に使っているのだが、実際はそうではないという認識。 そして let はブロックスコープなんだけどこれ同じ変数へ代入しているよねという先入観が組み合わさるとこの挙動の違いに違和感が出てくる。

ということで var を手放しで let にすればいいというわけではないということ。

メモ

クロージャとして切り取られたスコープは、その作られた場所でのスコープで解決される。

実行時に外部から値を突っ込む場合は引数で取るしかない。

つまり変数名を一致させても、thisで書いてもダメということ。

Tags

javascript/basic/function/closure.txt · 最終更新: 2019-06-10 12:17 by ore