Learning to Code

プログラミング勉強記録

【JavaScript】変数の宣言と上書きについて

・参考にした教材

今回はこちらのサイトの関数について解説されている項を参考にしてます。
JavaScriptについて網羅的に解説されている良サイト。

javascript.info

このページ⇓
Functions

・ローカル変数とグローバル変数

ざっくり言うと関数の外で宣言した変数は「グローバル変数」で、
どこでも参照可能。
関数のカッコ{}の中で宣言した変数は「ローカル変数」で、
関数の中からしか参照できないという理解。

ローカル変数は外から参照できないという例:

function showHello () {
  let hello = "Hello, I'm JavaScript!"; // これがローカル変数
  alert( hello );
}

showHello(); // Hello, I'm JavaScript!

alert( hello ); // 参照できないのでエラー

関数内からグローバル変数の参照は可能:

let userName = 'Taro';

function showMessage() {
  let message = 'Hello, ' + userName;
  alert(message);
}

showMessage(); // Hello, Taroとなる。グローバル変数を参照できている

関数内でグローバル変数と同じ名前の変数を使うと、値の上書きも出来てしまう:

let userName = 'Taro';

function showMessage() {
  userName = 'Kenta';
  let message = 'Hello, ' + userName;
  alert(message);
}

alert(userName); // 関数が使われる前まではTaro

showMessage(); // Hello, Kenta

alert(userName); // 関数によってuserNameが上書きされたため、Kentaになる

ややこしいのが、この時に関数内でletを使っているとグローバル変数の値は上書きされない点。
ローカル変数として宣言した場合、例え同名でも別個の変数として扱われるため、
それが優先して使われる。グローバル変数には何も影響を与えない。 宣言なしで使用すると上記の例のように上書きされてしまう。
そもそも同じ名前の変数を複数宣言するということ自体があまり良くないのだとは思うが・・・

・ローカル変数によるグローバル変数の参照

また、ローカル変数の値に同名のグローバル変数を参照することは出来る。 1つ前の例のように、値も上書きされる。

let userName = 'Taro';

function showMessage() {
  userName = 'Kenta, ' + userName; // Kenta, Taroになる
  let message = 'Hello, ' + userName;// Hello, Kenta, Taro 
  alert(message);
}

alert(userName); // 関数が使われる前まではTaro

showMessage(); // Hello, Kenta, Taro

alert(userName); // 関数によってuserNameが上書きされたため、Kenta, Taroになる

ただし、グローバル変数を参照して関数内でローカル変数を宣言しようとするとエラーになる。

let userName = 'Taro';

function showMessage() {
  let userName = 'Kenta, ' + userName; // Kenta, Taroになる
  let message = 'Hello, ' + userName;// Hello, Kenta, Taro 
  alert(message);
}

alert(userName); // 関数が使われる前まではTaro

showMessage(); // エラー発生 

VM172:2 Uncaught ReferenceError: userName is not defined
    at showMessage (<anonymous>:2:30)
    at <anonymous>:1:1
・関数の引数はグローバル変数を上書きしない

どういうことかと言いますと、

function showMessage(userName, text) {
  userName = '*' + userName + '*';
  alert( userName + ': ' + text );
}

let userName = "Taro";

showMessage(userName, "Hello"); // *Taro*: Hello

alert( userName ); // Taroのまま。*Taro*に上書きはされない。

なぜこういう動きになるか、いまいち納得のいく解説は見つけられなかったが、
今回の場合はグローバル変数と同名なのが引数だからだと考えている。
userName = '*' + userName + '*';の行では、 1つ目のuserNameはあくまで引数で、そこに2つ目のuserName(グローバル変数)をあてはめている。 関数を呼び出した時だけ作られる引数だから、グローバル変数には影響を与えないのではないかと思う。

・教訓

・変数名は慎重に、わかりやすくつける(重複は避ける)
・変数はわざとでない限りは宣言して使う(そのほうが予期せぬエラーが少なくなる)
・引数は宣言しなくてよい

ということになるかと思う。