スポンサーリンク

JavaScriptの動かないコード (初級編) switch文で,数値以外の変数を評価した時のエラー


以下のJavaScriptコードが意図した動作をしないのは,なぜですか。(制限時間1分)


やりたい事:

  • 氏名を選択して,ボタンを押す。
    • ⇒氏名に対応する苗字と,苗字に対応するメッセージが表示される。
<body>

氏名を選んでください。

<select id="hoge">
	<option>鈴木</option>
	<option>鈴木 太郎</option>
	<option>鈴木 次郎</option>
	<option>佐藤</option>
	<option>佐藤 太郎</option>
	<option>佐藤 次郎</option>
</select>


<input type="button" onclick="f()" value="クリックしてメッセージを表示">



<script language="JavaScript">

function f(){
	// セレクトボックス要素
	var elem_select = document.getElementById( "hoge" );
	
	// 選択されている氏名を取得
	var str = elem_select.options[ elem_select.selectedIndex ].innerHTML;
	
	// 半角スペースのあとに名前が書いてあるか?
	if( str.match( /^.* .*$/ ) )
	{
		// 半角スペースよりも前の部分を取り出し,苗字だけにする
		str = str.match( /^[^ ]*/ );
	}
	
	// 苗字を表示する
	alert( "苗字は" + str );
	
	
	// 苗字に応じてメッセージを表示
	switch( str )
	{
		case "鈴木" :
			alert( "日本で最も多い苗字です。" );
			break;
			
		case "佐藤" :
			alert( "日本で2番目に多い苗字です。" );
			break;
			
		default : 
			// 「鈴木」でも「佐藤」でもない場合
			alert( "エラー:異常値です。" );
	}
}

</script>


</body>





答え




発生する不具合


「苗字は〜〜」というアラートは,正常に表示される。

  • 「苗字は鈴木」
  • 「苗字は佐藤」

ここまではOK。


しかし,プルダウンで選んだ内容によっては,その次に

"エラー:異常値です。"

が表示される。


「鈴木」と「佐藤」を選んだ場合はOKだが,
「鈴木太郎」「鈴木次郎」「佐藤太郎」「佐藤次郎」を選んだ場合は,動作がおかしい。


理由

まず,matchの使い方がおかしい。

"abc".match( "a" )

の実行結果は,

"a"

という文字列型の変数ではなく,

[ "a" ]

という配列である。


「matchの返り値が文字列」と誤解していると思われる。

実際には,この関数は下記のような挙動をする。

 >>> "abc".match("a")
 ["a"]


 >>> "abca".match("a")
 ["a"]


 >>> "abca".match(/a/)
 ["a"]


 >>> "abca".match(/a/g)
 ["a", "a"]


 >>> "123".match(/\d/)
 ["1"]


 >>> "123".match(/\d/g)
 ["1", "2", "3"]


 >>> "123".match(/\d*/g)
 ["123", ""]


熟読すべきURL:

JavaScript正規表現メモ。
http://d.hatena.ne.jp/koseki2/2009053...

  • 全てのマッチした箇所を得る。gオプションを付けてString#matchを使う。


上記のような誤解をしていても,気付かないかもしれない。

JavaScriptは自動的に「型変換」を行なうからだ。


alert() は裏で型変換を行ない,引数を文字列に変換してからダイアログ上に表示する。

alertの引数が配列の場合は・・・

 >>> ["a"].toString()
 "a"

このように,文字列になる。

それで,alertは,たとえ引数が配列だったとしても,正常に文字列を表示する事ができる。



なので,気付かない。

まるで「matchメソッドから文字列が返却された」かのように錯覚してしまう。


これはまずい。あとで,なし崩し的にコードが動作しなくなる可能性がある。


悪いサンプルの例:

JavaScript Reference
http://www.scollabo.com/banban/jsinde...

  • matchメソッドの返り値をそのまま文字列とみなして操作している


型変換を自動的に行なってくれない例として,switch文が挙げられる。


switch文の変数評価の仕組みも知っておく必要がある。

switch文は,引数とcase句が「厳密に等しい」ことを要求する。

switch文
http://www.ajaxtower.jp/js/if/index4....

  • 式と値が一致するかどうかは「==」演算子ではなく「===」演算子によって比較されます。この演算子の違いについては「等価演算子」を参照して下さい。

ECMAScript仕様書 switch文のBNF
http://www2u.biglobe.ne.jp/~oz-07ams/...


冒頭のコードで言うと,matchの返却値がArrayであるにも関わらず,switch側ではStringと比較している。

型を考慮した比較の場合,これだと決して等価にはならず,予期したcase部が実行されない事になる。



回避策

matchを利用した行の処理が配列ではなく文字列を返すよう,以下のように修正する。

// 半角スペースよりも前の部分を取り出し,苗字だけにする
str = str.match( /^[^ ]*/ );

 ↓

str = str.match( /^[^ ]*/ )[0];

補足

壮絶?!名字ランキング ランキング1位〜1000位
http://www.alles.or.jp/~tsuyama/za1.htm

  • 鈴木,佐藤,田中,小林,高橋(2000年)