2015/04/25

[javascript]複数の関数で扱う変数の宣言

艦これ・制空値計算スクリプトの制作の最後の最後で引っかかったのが表題の通り、複数の関数に渡って使用される変数をどうすればいいのかという問題だった。
具体的には以下の部分だ。

function loadsubmit(shipnum,lnum1,lnum2,lnum3,lnum4){
 switch(parseInt(shipnum)){
  case 1:
   loadArray1[0] = lnum1;
   loadArray1[1] = lnum2;
   loadArray1[2] = lnum3;
   loadArray1[3] = lnum4;
   break;
  case 2:
   loadArray2[0] = lnum1;
   loadArray2[1] = lnum2;
   loadArray2[2] = lnum3;
   loadArray2[3] = lnum4;
   break;
  case 3:
   loadArray3[0] = lnum1;
   loadArray3[1] = lnum2;
   loadArray3[2] = lnum3;
   loadArray3[3] = lnum4;
   break;
  case 4:
   loadArray4[0] = lnum1;
   loadArray4[1] = lnum2;
   loadArray4[2] = lnum3;
   loadArray4[3] = lnum4;
   break;
  case 5:
   loadArray5[0] = lnum1;
   loadArray5[1] = lnum2;
   loadArray5[2] = lnum3;
   loadArray5[3] = lnum4;
   break;
  case 6:
   loadArray6[0] = lnum1;
   loadArray6[1] = lnum2;
   loadArray6[2] = lnum3;
   loadArray6[3] = lnum4;
   break;
  default:
   break;
 }
}
function submitAircraft(shipnum,slonum,toair){
 switch(parseInt(shipnum)){
  case 1:
   toAir1[slonum-1] = toair;
   break;
  case 2:
   toAir2[slonum-1] = toair;
   break;
  case 3:
   toAir3[slonum-1] = toair;
   break;
  case 4:
   toAir4[slonum-1] = toair;
   break;
  case 5:
   toAir5[slonum-1] = toair;
   break;
  case 6:
   toAir6[slonum-1] = toair;
   break;
  default:
   break;
 }
}
function calc(){
 var result = 0;
 for(i=0; i<4; i++){
  result += Math.floor(Math.sqrt(loadArray1[i])*toAir1[i]);
  result += Math.floor(Math.sqrt(loadArray2[i])*toAir2[i]);
  result += Math.floor(Math.sqrt(loadArray3[i])*toAir3[i]);
  result += Math.floor(Math.sqrt(loadArray4[i])*toAir4[i]);
  result += Math.floor(Math.sqrt(loadArray5[i])*toAir5[i]);
  result += Math.floor(Math.sqrt(loadArray6[i])*toAir6[i]);
 }
 var ele = document.getElementById('result');
 ele.innerHTML = '対空値は'+result+'っぽい';
}

対空値は艦載機の積載数の平方根とその艦載機の対空値の積によって求めることができる。よって艦載機の積載数の行列loadArraykと艦載機の対空値を代入した行列toAirkを6隻分用意し(ともにkは艦の番号。1≦k≦6)、calc関数でそれらふたつの行列のi個目(0≦i≦3)同士を掛けて足しあわせていくことにした。
その際、当初は艦名を選択した際に実行されるsetShipname関数から、艦の番号(何隻目か、ということ)とその積載数(最大4スロット)を引数として呼び出されるloadsubmit関数内でloadArraykを、艦載機を選択した際に実行されるsetAircraft関数から、艦の番号、スロットの番号、対空値を引数として呼び出されるloadaircraft関数内でtoAirkを宣言し、代入していた。
しかしそれではcalc変数からそれら2つの行列を呼び出しても、読み込むことができなかったのだ。

そこで色々と調べた結果、変数のスコープ (JavaScript)に行き着いた。これによればjavascriptにおいてはプログラム内のどこからでも、即ち複数の別の関数から扱えるグローバル変数と、関数内で宣言し、その関数でしか扱えないローカル変数がある、ということらしい。
 つまり、先ほどの宣言の仕方では、loadArrayはloadsubmit関数、toAirはloadairctaft関数でしか扱えなくなっている、ということだ。2つとも関数内で宣言しているため、ローカル変数となっているからだ。
 その対処として、これら2つの行列をグローバル変数として宣言する必要がある。つまり、関数の外で宣言すればいいということになる。

//グローバル変数宣言
var loadArray1 = [0,0,0,0];
var loadArray2 = [0,0,0,0];
var loadArray3 = [0,0,0,0];
var loadArray4 = [0,0,0,0];
var loadArray5 = [0,0,0,0];
var loadArray6 = [0,0,0,0];
var toAir1 = [0,0,0,0];
var toAir2 = [0,0,0,0];
var toAir3 = [0,0,0,0];
var toAir4 = [0,0,0,0];
var toAir5 = [0,0,0,0];
var toAir6 = [0,0,0,0];
(後略)

上述のリンク先によれば、関数内で宣言する場合にも「var」をつけずに宣言すればグローバル変数として扱われるとの記述もあったが、プログラムの冒頭でまとめて宣言しておいたほうが、どの変数はすべての関数で扱えるのか後でわかりやすいかと思い、最初にまとめて宣言することにした。
javascriptはJavaを勉強した時とは違い、実際にスクリプトを作りつつ勉強しているのでそもそも「スコープ」という言葉を知らず、上記のリンク先に行き着くにも少し時間がかかった。
最初は一瞬、面倒な仕様だと思ったが、ローカル変数として宣言した変数は同じ名前で違う関数のローカル変数として宣言することができ、変数名に規則性を持たせることができるため、なかなか使いやすい。(このプログラムでは「selected」というローカル変数がいくつかの関数で使われている。)

まとめ

  • Javascriptの変数は、変数を扱える範囲の違いによってグローバル変数とローカル変数に分けられる。
  • 変数外で宣言するか、varをつけず宣言するグローバル変数はプログラム内どこからでも扱える。
  • 変数内で宣言するローカル変数は宣言した関数内でしか扱えない。
  • 複数の関数で扱う変数は、グローバル変数として宣言しなければならない。

0 件のコメント:

コメントを投稿