スコープというのは、範囲とか、視野とかそういう意味があります。
変数のスコープというのは変数使用できる範囲のことです。
例を挙げて説明します。そうですね、学校で考えましょう。
学校のクラス(3組とかA組等)には出席番号がありますね。
「出席番号1番」を人を入れる箱とイメージしてください。
スコープ=範囲
ここで問題です。
3組の出席番号1番とA組で出席番号1番
を呼び出した場合、呼ばれる人は同じでしょうか?
そうです。同じ人ではないですね。
別の人がハーイって現れるはずです!
これは出席番号の有効範囲がその「組」の中だけだからです。
なんとなくイメージつきましたか?
javascriptの変数も出席番号と同じように使用できる範囲が決まっています。
覚える必要のある範囲は次の2種類覚です。
- ローカル変数
- グローバル変数
順番に説明していきます!
ローカル変数とは
ローカルです。
ローカル番組とか使いますよねあのローカルです。より限定された変数(使える範囲が狭い)という意味ですね。
抽象的でイメージが湧きにくいと思うので、
ローカル変数をプログラムから確認してみましょう。
どんな結果になるか考えてみてください。
1 2 3 4 5 6 7 8 |
var name = 'ビリー' function showName(){ var name = 'ジョニー'; return name; } alert(name); |
上のプログラムは変数を2つ作ってますね。
一方がローカル変数、もう一方がグローバル変数です。
どちらか予想して実行してみてください。
そして、最終行で実施しているalert文で何が表示されるか想像してみてください。
実行できましたか?
そうです、「ビリー」がアラートで表示されますね。
1行目で作成した変数nameをshowName関数の中で「ジョニー」に上書きしていますが、結果は「ビリー」のままです。
現象を整理すると、関数の中で宣言した変数は関数の外では利用できないことが
わかると思います。この関数内で宣言された変数をローカル変数と言います。
関数で囲われた間は1つの組みたいになるわけです。
もちろん、関数の中で宣言したローカル変数は関数の中でしか利用できません!
これも出席番号と同じですね。
では、次のプログラムを実行してみましょう。
1 2 3 4 5 6 7 8 9 |
var name = 'ビリー'; function showName(){ var name = 'ジョニー'; alert(name); return name; } showName(); alert(name); |
先ほどのサンプルの5行目にアラート文を追加しました。
これを実行すると何が出るでしょう?
正解は最初に「ジョニー」次に「ビリー」が出力されます。
処理の流れは追えてますか?一度整理しましょう。
- 1行目でnameにビリーが代入されます。
- showName関数が呼び出されます。
- 関数内でnameが作られ、ジョニーが代入されます。
(関数の中と外でname変数ができますが、それぞれ別のものです) - showname関数のalert文が実行されジョニーが表示されます。
- 9行目のalert文が実行されます。ここのnameは1行目で宣言したnameになります。
ざっと図式すると下記のような感じです。
簡単に言ってしまうと、関数で分けられている場合、関数の中と外で
同じ名前の変数が作れちゃうってことです。作りたい放題です。
ちなみに関数の外で宣言された変数をグローバル変数と呼びます。
関数内で宣言されたはローカル変数と同じグローバル変数がある場合、
ローカル変数が優先されます。
では実際にローカル変数がない場合はどうなるか実際に試してみましょう。
プログラムは下記のようになります。
1 2 3 4 5 6 7 8 9 |
var name = 'ビリー'; function showName(){ //var name = 'ジョニー'; //ローカル変数をコメント alert(name); return name; } showName(); alert(name); |
結果は2回ともビリーが表示されます!
繰り返しとなりますが、関数外で宣言された変数は関数内でも
利用することができます。
なおプログラムの流れは下記の通りです。
- 1行目で変数nameにビリーを入れます。
- 8行目のshowName関数が呼び出されます。
- showName関数のalert文のnameは1行目のname変数を参照します。
- showName関数のalert文でビリーが表示されます。
- 9行目のalert文でビリーが表示されます。
図で表すと下記のようになります。
関数外で宣言したグローバル変数は関数内からも参照できることがわかりますね!
次です。変数の宣言について、いつもと書き方を変えてみます。
1 2 3 4 5 6 7 8 9 |
//var name = 'ビリー'; //グローバル変数をコメント! function showName(){ name = 'ジョニー'; //いつもはvarを前につけてるけど今回はつけない。 alert(name); return name; } showName(); alert(name); |
結果はどうなると思いますか?
やったことないのでわからないと思いますが、予想してみてください!
「ローカル変数だから最初のアラートはジョニーで次のアラートでは何も表示されない」と回答をした場合は今までの内容が理解できています。バッチリです。
でも今回は違います。実際はジョニーが2回アラートされます。
今まで変数をつけるとき何気なく「var」をつけていましたが、
実はvarをつけないで変数を宣言した場合は関数内でもグローバル変数となります。
プログラムの流れは次の通りです。
- showName関数が呼び出されます。
- グローバル変数nameにジョニーが入ります。
- showName内のアラートによりジョニーが表示される
- 9行目のalert文によりジョニーが表示される。
図で表すと下記の通りです。
さて、ローカル変数とグローバルが理解できたところで、問題です。
ローカル変数とグローバル変数が分かれている理由はなんでしょうか。
逆に分かれていない場合どんな問題が起きるでしょうか。
グローバル変数だけでプログラムを作成した場合
すべての関数がすべての変数の値を参照できるため、一見便利そうに思えますが
実はそうでもないのです。
例えば、大規模なプログラムを作る場合、いくつかの機能に分けて作業を分担します。その際、もしすべてがグローバル変数の場合、お互いに変数名が被らないように注意しなければなりません。
そうすると、使用できる変数名がどんどんなくなっていきます。
これは名前空間の汚染なんて呼ばれたりします。
ローカル変数で完結できるものはなるべくローカル変数で完結するようにするよう心がけましょう。
ローカル変数だけでプログラムを作成した場合
ローカル変数だけの場合つまり、変数の有効範囲が変数内だけの場合は
プログラム上あまり変更する予定がない値、消費税、円周率と言った数値も
毎回関数内で宣言することになりますが、その値に変更があった場合、すべての関数を修正する必要があります。
また、余計な引数が多くなりプログラムが煩雑になる恐れがあります。
巻き上げ(ホイスティング)
お金の巻き上げです。嘘です。
このトピックはちょっとややこしいです。
まずはプログラムで見てみましょう。
次のプログラムで2つのアラート文がありますが、
どのような内容が表示されるか予想してみましょう。
1 2 3 4 5 6 7 8 9 |
var name = "ビリー"; function showName() { alert(name); //アラート1 var name = "ジョニー"; alert(name); //アラート2 } showName(); |
これまでの学習の内容をふまえると、アラート1は「ビリー」、アラート2は「ジョニー」となりますが、今回は違います。
アラート1はundefined、アラート2はジョニーになります。
不思議ですね。。
実はjavascriptの関数内で変数を宣言する場合、どの場所で書いても関数の先頭で実行されるという決まりがあります。
つまり上のプログラムは次のようになります。
1 2 3 4 5 6 7 8 9 10 |
var name = "ビリー"; function showName() { var name; //巻き上げられて関数の先頭で宣言される alert(name); //アラート1(nameには何も入ってないのでundefined) name = "ジョニー"; alert(name); //アラート2(直前に代入された「ジョニー」を表示) } showName(); |
一番下で変数を作っても上まで巻き上げられるのです。
お金じゃなくて変数の宣言が巻き上げられるのです!
ホイスティングとも呼ばれます。
まとめ
- ローカル変数は関数の中で宣言したもので、関数の中でのみ有効な変数
- グローバル変数は関数の外で宣言した変数、またはvarをつけないで宣言した変数で、すべての関数から参照することができる。
- 関数内で宣言される変数はは必ず関数の先頭で実行される
次のプログラムを実行した場合にどのような結果が出るか
予想して実際に確かめてください。
-
123456789var name = 'ビリー';function showName(){name = 'ジョニー';alert(name);return name;}showName();alert(name);
-
123456789var name = 'ビリー';function showName(){alert(name);return name;var name = 'ジョニー';}showName();alert(name);
商品の金額が格納された配列(productsPrice)
から、商品の消費税込みの合計値段と消費税を含まない合計を引き算して
消費税の金額をアラートするプログラムを作ろうと
していますが、どうもうまく動かないです。
このプログラムを修正して正しい税込合計金額が表示されるようにしてください。合わせて、グローバル変数とローカル変数にした方が良い変数があれば
修正してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var productsPrice = [100,200,500,800]; var result = getTotalPrice(productsPrice) - getTotalPriceIncludeTax(productsPrice); alert(result); function getTotalPrice(argArray){ var totalPrice = 0; for(i = 0; i < argArray.length; i++ ){ totalPrice += argArray[i]; } return totalPrice; } function getTotalPriceIncludeTax(argArray){ var totalPrice = 0; while(i < argArray.length){ totalPrice += argArray[i]; i++; } return totalPrice*1.08; } |