6時だョ!!全員集合!!

Rails・JavaScrictを中心にアウトプットします。

2021年12月21日 JavaScript (JS Primer) 関数とスコープ

静的スコープ:
ある変数がどの値を参照するかは静的に決まる メモリ管理の仕組み: 参照されなくなったデータはガベージコレクションにより解放されます。

クロージャーとはこの2つの仕組みを利用して、関数内から特定の変数を参照し続けることで関数が状態を持てる仕組みのことを言います。

クロージャーがなぜ動くのか

const createCounter = () => {
    let count = 0;
    return function increment() {
        // `increment`関数は`createCounter`関数のスコープに定義された`変数`count`を参照している
        count = count + 1;
        return count;
    };
};
// createCounter()の実行結果は、内側で定義されていた`increment`関数
const myCounter = createCounter();

console.log(myCounter()); // => 1
console.log(myCounter()); // => 2
  • 変数を保持するにはincrement関数の外側で変数countを定義します。createCounter関数スコープにあるcount変数を参照しているためです。
  • 変数に格納してcreateCounter関数を複数回呼び出すことで挙動が確認できます。
  • createCounterの結果をmyCounterに代入する事で、createCounterの返り値の1を参照するものがあるため解放されずに保持されます。

コード例2

function closure(initVal){
  var count = initVal;  // 100

  var innerFunc = function() {
    return ++count; // 1加算
  }
  return innerFunc;
}

var myClosure = closure(100);
console.log(myClosure()); // 101
console.log(myClosure()); // 102
  • closure関数はinnerFuncを返します。
  • innerFuncは変数であるinitValを参照します。これによりinitValの値はガベージコレクションにより関数内ではメモリが開放されません。
  • 結果はmyClosureに格納します。
  • myClosureはグローバル変数なため、グローバルオブジェクトが存在し続ける限り解放されることがないので、ローカル変数countも破棄されません。
  • countは破棄されないので、closure呼び出し時に代入された値が保持されることよってcountは加算されていきます

メモ 

  • クロージャーとは関数が特定の変数を参照することで関数が状態を持っていることを指します。
  • createCounter関数を実行するたびに、それぞれcountとincrement関数が定義されます。

参考

JS primerクロージャーがなぜ動くのか