質問: Date.now(); countUp(); }); ``` あらかじめブロック外で変数startTimeを宣言しておいて、クリックイベント内で変数startTimeに新しく値を再代入する。 その後、countUp関数で再代入した値を参照していますが、なぜ関数内で値が参照できるのか理解ができません。 **値を参照する際は、ブロック内で再代入された値が優先されているのでしょうか?** また、**ブロック外で変数の宣言のみをすることに何か意味などあるのでしょうか?** 基礎に関する質問で申し訳ないのですが、ご教授の程よろしくお願い致します
回答: > > 値を参照する際は、ブロック内で再代入された値が優先されているのでしょうか? > > > 着目すべきは「値」ではありません。 * 変数は宣言するとメモリ上に値を格納できる場所を確保するものです。 代入は「メモリ上に確保した場所」を参照して、値を入れ替えています。 コンピュータのハードウェアレベルでメモリと値の関係をざっくり説くと 1. メモリの指定アドレスに対して値を格納する(代入/書き換え/削除)。 2. メモリの指定アドレスに対して格納された値を参照する。 といった動作があります。マシン語(機械語)/アセンブリ言語では、名前の付いた変数はなく、メモリ番地(アドレス)を指定して値を扱います。 JavaScriptなど、(人が理解しやすい)高級言語では、メモリ番地を指定することなく処理がかけるように変数という仕様がありますので、**再代入された値**ではなく、**代入先のメモリ番地**を参照しています。 JavaScriptのスコープの概念は ブロックの内側にあるものを優先して利用しますが、次のように解釈するのが正しいと思います。 ``` let a = 10; // =メモリ上に新しい場所を確保(1) let b = 20; // =メモリ上に新しい場所を確保(2) (function(){ let a = 5; // =メモリ上に新しい場所を確保(3) // 同じ名前の変数を宣言しているが、メモリ番地は別 b -= 10; // ブロック内にないので、外で宣言された // b が指すメモリ番地に変更を加える console.log( a, b ); // 5, 10 })(); console.log( a, b ); // 10, 10 ``` > > ブロック外で変数の宣言のみをすることに何か意味などあるのでしょうか? > > > 上述した解釈で、ご質問にも示される ~~setTimeout の第一引数(コールバック関数)~~ *ストップウォッチのコード* を眺めてください。 ~~関数内で宣言すると、カウントできません。~~ 次のようなコードになります。 ``` const start = document.getElementById('start'); // 1)メモリ番地を(とりあえず)確保 let startTime; function countUp() { // 3)以降繰り返し. startTime のメモリを参照し格納済みの値を読む console.log(Date.now() - startTime); setTimeout( () => { countUp(); // 再帰呼び出し }, 10); } start.addEventListener('click', () => { // 2)startTime のメモリに Date値を格納 startTime = Date.now(); countUp(); }); ``` JavaScriptは、様々な処理を行うために非同期で実行する処理も多く、結果を受け取る手段として、ブロックの外で宣言する変数が必要になります。