JavaScriptの基礎知識記事です。
「とりあえずconstで定義しておけば、再宣言・再代入できないから安心なんでしょ」と、今日の昼までは思っておりました。
しかし、その浅い認識でエラーが出てしまったので、備忘録として書いておきます。
同じように思っているJavaScript初心者の方に読んでいただければと思います。
スコープ問題
console.logクイズ-何が表示されるでしょう?
①グローバル変数を関数内で読込
以下のコードを実行するとどうなるでしょう?
const drink = "coffee";
const bitter = () => {
console.log(drink);
};
bitter();
答えは、
coffee
です。
②グローバル変数と同名の変数を関数内で宣言
では、次の場合には?
const drink = "coffee";
const sweet = () => {
const drink = 'cocoa';
console.log(drink);
};
sweet();
答えは、
cocoa
です。
さっきはconstで代入していた変数を関数内でそのまま呼び出せたのに、関数内でもう一回同じ変数名で宣言できる!
③グローバル変数で定義した変数を、関数内で使用してから宣言
では最後に、次の場合はどうなるでしょう?
const drink = "coffee";
const salty = () => {
console.log(drink);
const drink = 'kobucha';
console.log(drink);
};
salty();
答えは・・・
Uncaught ReferenceError: Cannot access 'drink' before initialization
エラーが返ってきます。
JavaScriptの仕様として、グローバル変数は関数内やブロック内でも読み込めますが、グローバル変数と同じ名前の変数を、関数やブロック内で宣言することもできます。
このあたり見るとわかるかと↓
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/block
そのため、①、②のような挙動になりました。
それでは、なぜ③でエラーが返ってきたのか。
最初のconsole.logでcoffeeと表示され、次のconsole.logでkobuchaと表示されても良いのでは?
JavaScriptではブロック内にconstがあると、書いた順番に関係なくそのブロック内の変数はすべてその定義された変数で上書きされてしまいます。ブロック内で定義した時点で、同じ変数名のグローバル変数はブロック内では読み込まれなくなります。
ただし、あくまでも上書きされるのは変数の存在だけです。代入処理は上から順番におこなわれていきます。だから「その変数は定義されていないよ」というエラーではなく、「その変数は初期化されていないよ」というエラーが返ってくるのです。
結論
スコープが変わると、constで定義していても同名で定義できてしまいます。グローバル変数と同じ名前の変数を関数内で定義してもエラーは出ずに上書きされてしまいます。気をつけようと思いました。
リスト・オブジェクト
ついでに、constで定義したリストとオブジェクトの扱いも勘違いしがちかなと思ったので触れておきます。
const drink = ['coffee','cocoa'];
drink.push('kobucha');
console.log(drink);
const food = {
lunch: 'peperoncino',
dinner: 'karaagekun'
};
food['breakfast'] = 'protain';
console.log(food);
["coffee", "cocoa", "kobucha"]
{lunch: "peperoncino", dinner: "karaagekun", breakfast: "protain"}
リストやオブジェクトの中身をいじる処理は自由にできます。もちろん、再宣言しようとしたり、以下のようにリスト自体、オブジェクト自体を再代入しようとするとエラーが出ます。
const drink = ['coffee','cocoa'];
drink = ['coffee','cocoa','kobucha'];
以上です。
ちなみにたいていのパスタは昆布茶を入れるとおいしくなります。
それでは。