どうも、かわうそです!
今日は『ブロックスコープについて letとconstも合わせて解説』をしていきます。
こんな方におすすめです。
- letとvarの違いが分からない
- ブロックスコープと言われてもあまりピンとこない
- スコープという言葉はなんとなく分かるかも
スコープについて復習が必要かも?と思った方は、こちらの記事がおすすめです。
イラストや動画を使い、分かりやすい(ざっくりな)説明を心がけます。
そもそもブロックスコープとは
そのまんま、『そのブロックに作られたスコープ』ですね。
じゃあなにが『ブロック』かというと、{ }
』 この 波括弧(なみかっこ)です。
(中括弧ともいうかもです。)
if(){}
とかfor(){}
でよく見かけるあいつです。
意味的には、複数行の処理をひと固まり=ブロックとして扱う、ということです。
forやらifがなくても、
1 2 3 4 5 6 7 |
{ //これだけでもブロック let a = 1; let b = 2; let c = 3; let d = a + b + c ; } |
波括弧だけでも、ブロックは作れるんですよ!
そしてスコープとは、定義された変数や関数を『参照できる範囲のこと』です。
『ブロックによって、参照できる範囲が決めれらちゃったよ』
ってのが、ざっくりとした『ブロックスコープ』ですね。
なにがブロックで、なにがスコープかは分かった!
『どうやってブロックスコープができるのか』教えなさいよ!!ですね。
例文を使いながら説明していきます。
ブロックスコープを作ってみよう
ブロックスコープを作るには、『ブロックの中でスコープ作りま~す』と宣言しないとJSさんには伝わりません。
俺はここに自分だけの世界を作るんだ!!!!
・・・そんなブロックスコープを作る宣言をしてくれるのが、let
およびconst
の変数宣言です。
変数宣言っていうのは、みなさんやってるあれです、あれ
1 |
let name = "kawauso" // letで変数宣言した |
let?const?なんじゃそりゃって方、後で説明していきます。
とりあえず例文を見てみましょう
1 2 3 4 5 |
if (true){//ブロック let name = "かわうそ"; //ブロックスコープ発生!!! console.log(name) //かわうそ } console.log(name) //not defined |
ブロックの外からname
を参照できませんね。
では、今まで通りvar
で宣言した場合はどうなるでしょうか。
1 2 3 4 5 |
if (true){//ブロック var name = "かわうそ"; //今まで通り console.log(name) //かわうそ } console.log(name) //かわうそ |
ブロックの外でもname
を参照できていますね。
つまり、let
では『ブロックスコープ』が生まれているが、var
ではブロックスコープが生まれていません。
もちろん、let
をconst
に変えても、同様にブロックスコープは発生します。
ちなみにこちらも試しておきましょう。
1 2 3 4 5 6 |
if (true){//ブロック let name1 = "kawauso"; //ブロックスコープ発生 var name2 = "かわうそ"; //今まで通り } console.log(name1) //not defined console.log(name2) //かわうそ |
ブロックスコープが発生しているのはやはりletだけです。
var を let に変えるだけで、スコープを作れちゃんですね~!
というわけで、ポイントになっている『letとconst』について、説明していきます。
letとconstとvarの違い
『letとconst』はvarと同じく変数宣言のキーワードです。
ECMAScript2015から採用された、新しめの変数宣言方法です。
その中でもconst
は、定数に対してのみ使用されます。
つまり、 変更されない、値が更新されないものに対して使用する変数(定数)宣言となっています。
let
はブロックスコープを作れますので、var以上に保守性のある記述が可能になります。
もう少し分かりやすいように、それぞれの違いをざっくり表にまとめました。
初期値省略 | 再代入 | 再宣言 | ブロックスコープ | |
var | 〇 | 〇 | 〇 | × |
let | 〇 | 〇 | × | 〇 |
const | × | × | × | 〇 |
初期値は、変数宣言時に値を設定しないといけないかどうか
再代入は変数の値を変更、更新できるかどうか
再宣言は、同じ変数名で、もう一度変数宣言できるかどうか、です。
各項目について詳しく見ていきます。
初期値の省略
下のコードをご覧ください。
1 2 3 |
var name1; //省略可 let name2; //省略可 const name3; //SyntaxError 省略不可能 |
constは初期値を必ず設定してあげましょう。
1 |
const name3 = "kawauso"; |
再代入
var
1 2 3 |
var name = "kawauso"; name = "かわうそ"; //再代入可能 console.log(name) // かわうそ |
let
1 2 3 |
let name = "kawauso"; name = "かわうそ"; //再代入可能 console.log(name) // かわうそ |
const
1 2 |
const name = "kawauso"; name = "かわうそ"; //TypeError 再代入不可 |
constは定数なので、再代入は不可能です。
再宣言
var
1 2 3 |
var name = "kawauso"; var name = "かわうそ"; console.log(name) // かわうそ |
let
1 2 |
let name = "kawauso"; let name = "かわうそ"; //SyntaxError |
varと違い、SyntaxError:すでに宣言されてます
的なエラーが返ってきます。
letを使用していれば、もし、たまたま同じ変数名を宣言してしまって(変数衝突)、値が更新されて重大なエラーが発生した・・・
なんてことが防げるわけです。
※補足です
ただし、スコープが変われば話は別です。
1 2 3 4 5 6 7 8 |
//グローバルスコープ let name = "kawauso"; { //ブロックスコープ let name = "かわうそ"; console.log(name)//かわうそ } console.log(name) //kawauso |
同じ変数nameなのに、再宣言できちゃってますね。
これは『スコープチェーン 』ってやつの仕業です。
入れ子が深い = ネストが深い スコープから、優先的に変数を参照して解決していく、JavaScriptの仕組みなのです。
もう1つ、ブロックスコープのネストを深くしてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//グローバルスコープ let name = "kawauso"; { //ブロックスコープ let name = "かわうそ"; { let name = "カワウソ"; console.log(name)//カワウソ } console.log(name)//かわうそ } console.log(name) //kawauso |
もう少し先、必ずみなさんが出会う相手です。
const
1 2 |
const name = "kawauso"; const name = "かわうそ"; //SyntaxError |
もちろん、constもlet同様、再宣言できません。
ブロックスコープ
var
1 2 3 4 |
{ var name = "kawauso"; } console.log(name) // kawauso |
var はスコープを作りませんので、参照可能です。
let
1 2 3 4 |
{ let name = "kawauso"; } console.log(name) // not defined |
letでの変数宣言はブロックスコープを作りますので、参照ができません。
const
1 2 3 4 |
{ const name = "kawauso"; } console.log(name) // not defined |
もちろん、constもlet同様、参照は不可能です。
変数の巻き上げ
最後に、変数の巻き上げについて、さらっとだけ説明しておきます。
まずはvarから。
変数の宣言前に、console.log(name)
で参照をしています。
1 2 |
console.log(name); //undefined var name = "kawauso"; |
結果はundefined
でしたが、エラーではありません。
宣言されていない変数を参照すればエラーになりそうなものです。
(他プログラム言語ではエラーになるので、きっとJavaScriptに頭を悩まされる要因の1つになるのでしょう)
ざっくりですが、これが『巻き上げ 』 っちゅーやつです!
次はletです。
1 2 |
console.log(name); //ReferenceError: name is not defined let name = "kawauso"; |
しっかりエラーになりました。もちろん、constでもletと同様です。
この『巻き上げ』を防ぐために、varでの変数宣言はスコープ内での先頭ですることが推奨されていました。
1 2 3 4 5 6 7 8 |
function () { var name = "kawauso"; var area = "kansai"; var like = "football"; console.log(`${name}は${area}に${like}のために旅立ちました`); } |
厳密には、letでもvarでもconstでも『巻き上げしている 』ことは変わりありません。
『undefinedというプリミティブ値を代入しているかどうか』だと、解釈しています。
最後に letを使おう。
というわけで、 ブロックスコープは少しでもご理解いただけましたでしょうか。
合わせて、やりたい放題だった『var』さんの危険性がご理解いただけたでしょうか。
様々な場面で、varと同じように使用できる かつ 適切に使用範囲と参照範囲を限定してくれる、let。
chrome, edge, safari, firefoxにはもちろん対応しているし、ie11にも対応しています。
インターネットエクスプローラーは、将来的に廃止が決まっているレガシーブラウザです。
特別な理由がない限りは、これからはvarではなくletを使っていきましょう。
また、letを使うからには『ブロックスコープ』も活用してみてくださいね!
ありがとうございました!