タグ別アーカイブ: JavaScript

PHP、JavaScriptにおける三項演算子(条件演算子)

ライブラリなどの出来合いコードを読解するようになると、割と目にすることも多い三項演算子(条件演算子とも)。条件分岐のifクローズをただの式に変えてしまうということで、その使用には抵抗がある向きも多いと思う(実際私もあまり好きではない)。ただ、三項演算子イコール単純なifクローズの省略記法というわけではなく、少々のメリットも存在する。その辺りを含めて、PHPとJavaScriptにおける三項演算子の使い方をまとめてみることにした。

三項演算子の基本的使用法

三項演算子については、PHP、JavaScriptともにC言語と同じ”?”や”:”といった演算子を使う。とりあえずPHPの場合で説明すると、

//ifクローズを使った場合
if($age >= 20){
$alcohol = "OK";
} else {
$alcohol = "NG";
}
 
//三項演算子を使った場合
//(式1) ? (式2) : (式3);
//(式1)が真であるとき(式2)、偽であるなら(式3)
$alcohol = $age >= 20 ? "OK" : "NG";

という具合になる。JavaScriptの場合は変数に$がつかない程度の違い。

三項演算子に慣れて親しんでいないと、直感的に理解できず少し混乱してしまう部分もあるかもしれない。上の例で見ると、変数$bへの代入で使われる”=”と、(式1)であるところの”$a <= 100"のような条件式で使われる"<="等の記号が字面上ややこしく見える。 三項演算子は算術演算子のように式を評価した結果を返すと考えなければならない。つまり、

$b = $a * 100;

こういった代入式と同じで、右辺を評価したが代入されているのだ。三項演算子は代入演算子より優先度が高い。

三項演算子に値ではなく式を入れる

ところで、三項演算子を用いた例として、以下のようなものもあり得る。PHPでの例。

//例1
$age >= 20 ? $alcohol = "OK" : $alcohol = "NG";
 
//例2
$age >= 20 ? print "OK" : print "NG";

これは、前回PHPの言語構造を説明した際に出てきた式とは何かというところで、式とは値をもつものという定義により、文法的にパスすると書いた例だ。もちろんJavaScriptの式の定義も同じであり、たとえば

//例1
age >= 20 ? alcohol = "OK" : alcohol = "NG";
 
//例2
age >= 20 ? alert("OK") : alert("NG");

このような式の代入が可能だ。

エルビス演算子(PHP5.3以降のみ)

PHPの場合、三項の真ん中の(式2)を省略して、(式1)がTRUEである場合値も(式1)になる書き方が出来る。この場合、”?:”という演算子を使う。時計回り90度回転したときに誰かさんの顔に見えるので、エルビス演算子と呼ばれる。

//(式1) ?: (式3);
//(式1)が真であるとき(式1)、偽であるなら(式3)
$tobaccoOkFlag = $alcohol == "OK" ?: 0;
 
//つまり、こう書いているのと同じ
$tobaccoOkFlag = $alcohol == "OK" ? 1 : 0;

ただし、PHP5.3以降でないと使えない。

三項演算子の右結合と左結合

三項演算子を多重に用いる場合、評価する順序が問題となってくる。標準的な順序は右結合と呼ばれる、右から評価していく方法。JavaScriptは右結合なので、JavaScriptの例を書く。

//右結合の場合
tobaccoOkFlag = pregnantFlag ? 0 : alcoholOkFlag ? 1 : 0;
 
//評価順序を分かりやすく書くと
tobaccoOkFlag = pregnantFlag ? 0 : ( alcoholOkFlag ? 1 : 0 );

この場合、多重に用いられた三項演算子のうち、右側のものが優先して評価される。alcoholOkFlagが0の場合は、pregnantFlag、つまり妊婦かどうかに関係なくtobaccoOkFlagは0になる。
一方、PHPはこれとは逆の左結合になるのだが、PHPの場合で解釈してみよう。

//左結合の場合
$tobaccoOkFlag = $pregnantFlag ? 0 : $alcoholOkFlag ? 1 : 0;
 
//評価順序を分かりやすく書くと
$tobaccoOkFlag = ( $pregnantFlag ? 0 : $alcoholOkFlag ) ? 1 : 0;

$pregnantFlagつまり妊婦であるかどうかを評価して、妊婦なら0、そうでないなら$alcoholOkFlagの値によって$tobaccoOkFlagが決まる。例があまり直感的なものに出来なかったので、申し訳ない気持ちでいっぱい。

左結合になる言語はほぼPHPのみ。というのも、PHPが左結合になってしまっているのは言語仕様を決めた際の間違いをひきずっているらしい。今更変えられないので、今後もPHPの短所として残り続けるだろう。

右結合の場合のif-elseの書き方

三項演算子が右結合だとif-elseがスッキリ書けるというメリットがある。右結合のJavaScriptの例。

//if-elseを使う場合
if(age >= 65){
message = "高齢者料金になります。";
} else if(age >= 20){
message = "大人料金になります。";
} else if(age >= 6){
message = "小人料金になります。";
} else {
message = "幼児料金になります。";
}
alert(message);
 
//三項演算子を使う場合
message = age >= 65 ? "高齢者料金になります。"
: age >= 20 ? "大人料金になります。"
: age >= 6 ? "小人料金になります。"
: "幼児料金になります。";
alert(message);

さらに、ifクローズが制御構造であるのに対して、三項演算子は式であるため、関数の直接の引数にすることも出来る。

//三項演算子を引数に
alert(age >= 65 ? "高齢者料金になります。"
: age >= 20 ? "大人料金になります。"
: age >= 6 ? "小人料金になります。"
: "幼児料金になります。");

ここまでスッキリするのなら、三項演算子を導入することも検討してよいのではないだろうか。
逆にPHPでは、三項演算子を多重に使用する事は避けた方が良いのかもしれない。


Conceptual:EXIFviewerに撮影地の地図表示機能を追加

予告通り、ローカル領域にあるjpegファイルのEXIF情報を表示するWEBサービス、EXIFviewerの機能を追加してみた。今回追加した機能は、jpegファイルのEXIFの中に撮影地情報が格納されていた場合、Google Mapsで表示するというもの。EXIFファイルの中のGPS情報をテーブルに表示するまでは前のヴァージョンでも実現していたため、今回の説明はGoogle Maps APIと連携する際の注意点が中心となる。

連携の前段階、既存プログラムの手直し

前のヴァージョンのEXIFviewerの作成手順では、表の成形部分で特にid指定などもしないセルを吐き出していた。今回、緯度経度情報をGoogle Maps APIに渡すには、JavaScript(jQuery)を使ったクライアントサイドでの値取得が必要となる。そのため、値の入ったセルにidで一意の名前をつけるようにする。また、前回手を付けられなかった値のサニタイジングもついでに行う。

if(!empty($_POST["path"])){
if($exif = exif_read_data($_POST["path"],NULL,true)){
echo "<h2>EXIF情報</h2>";
echo "<table class='exiftable table table-striped'><thead><tr><th>キー名</th><th>値</th></tr></thead><tbody>";
foreach ($exif as $key => $section) {
    foreach ($section as $name => $val) {
	if(is_array($val)){
	foreach ($val as $valname => $vval) {
	echo "<tr><td>" . htmlentities($key,ENT_QUOTES,"UTF-8") . ' ' . htmlentities($name,ENT_QUOTES,"UTF-8") . ' ' .  htmlentities($valname,ENT_QUOTES,"UTF-8") . "</td><td id=" . '"' . htmlentities($name . $valname,ENT_QUOTES,"UTF-8") . '">' . "$vval</td></tr>";
	}
	} else {
	echo "<tr><td>" . htmlentities($key,ENT_QUOTES,"UTF-8") . ' ' . htmlentities($name,ENT_QUOTES,"UTF-8") . "</td><td id=" . '"' . htmlentities($name,ENT_QUOTES,"UTF-8") . '">' . htmlentities($val,ENT_QUOTES,"UTF-8") . "</td></tr>";
	}
    }
}
echo "</tbody></table>";
} else {
echo "画像のEXIFデータが見つかりませんでした。";
}
} else {
echo "画像をセットして下さい。";
}

これがPHP部分。画像のEXIFにGPSの情報が不足なく入っていた場合に初めて処理を行うようにするので、このPHP部分で情報の不足をチェックし、地図表示の実行フラグとして非表示のセルにでも入れれば、JavaScript側の記述がスマートに済む筈。今回それをしなかったのは、不特定多数が利用する可能性があるため、出来るだけサーバ側の負担を軽く、クライアントサイドで処理を担保してもらうように設計したため。

Google Maps APIの使用方法

Google Maps APIを使いたい場合、htmlのヘッダ部で読み込む事になる。

<head>
.
.
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
</head>

こういった具合。クエリストリングのsensor=falseという部分は、利用者の位置情報を求めるかどうかということで、falseに設定しておけば鬱陶しい確認ダイアログが出ないだろう。
今回は、id=”googlemapdiv”というdiv要素の中にマップを表示する場合で説明する。jQueryのreadyの中に、以下のように記述した。

$(function(){
.
.
var option = {
zoom: 17,//地図の縮尺初期設定(0〜19)
center: new google.maps.LatLng(imagelat,imagelong),
//地図の中心の座標 LatLng(緯度,経度)
//とりあえず今回はimagelat,imagelngというユーザ定義変数を使った
//もちろんこれより前に宣言されてないといけない
mapTypeId: google.maps.MapTypeId.ROADMAP,//地図の種別初期設定
zoomControl: true,//ズームGUIの表示非表示
streetViewControl: true,//ストリートビュー表示可能にするか
scaleControl: true,//ズームスライダーGUIの表示非表示
mapTypeControl: true//地図の種別切り替えGUIの表示非表示
};
var map = new google.maps.Map($("#googlemapdiv")[0],option);
var marker = new google.maps.Marker({
				position : option.center,
				map : map
			});
});

コントロールの指定については必須ではない。google.maps.Mapメソッドに引数として地図の設置場所(DOMオブジェクトで指定)と、各種オプションを代入する。

Google Maps API用に緯度経度を10進法に変換する

さて、お膳立ても出来たので、単純にPHP側で成形した表のGPS情報の部分を取得し、Google Maps APIに渡せば良い、、とはいかない。
iPhone等で記録されるEXIFのGPSデータは、60進法で記録されているが、Google Maps APIが受け付けるのは10進法の数字なのだ。そこで、画像から取得した緯度経度を60→10進法に変換する処理を用意しないといけない。
60→10進法変換の方法だが、たとえば60進法で12.3という数字があった場合、10の位の1は10進法の60*1を表し、1の位の2は1*2、小数点第一位の3は60分の1*3を表している…といった数学の知識を、思い出してほしい。iPhoneなどが採用している60進法の表示方式では、”北緯36度46.5分0秒,東経137度54.52分0秒”といったように書くが、これは成形された表ではこのように表示されている。

EXIFでの緯度経度

EXIFでの緯度経度

GPSLatitudeRefやGPSLongitudeRefが東西南北(EWSN)を表している事は分かり易い。GPSLatitude 0が表すのは、緯度の”度”の部分。”36/1″という数字は、分母が1で分子が36ということ、つまり分数だ。同様に、GPSLatitude 1は100分の4650、つまり46.5分を表すといったように、面倒臭い書き方をしている。しかもこの値が60進法なのだ。
変換手順は、分数の文字列をなんとか少数に直して、それから10進法への変換を行うという形になる。
そして最後に10進法に直した後の値に、西経、南緯の場合マイナスをつけるという手順も必要だ。

 
//緯度を求める
var imagelat = parseFloat($("#GPSLatitude0").text().split("/")[0])/parseFloat($("#GPSLatitude0").text().split("/")[1]) + parseFloat($("#GPSLatitude1").text().split("/")[0])/(parseFloat($("#GPSLatitude1").text().split("/")[1])*60) + parseFloat($("#GPSLatitude2").text().split("/")[0])/(parseFloat($("#GPSLatitude2").text().split("/")[1])*3600);
if($("#GPSLatitudeRef").text() == "S"){
imagelat = "-" + imagelat;
}
 
//経度を求める
var imagelong = parseFloat($("#GPSLongitude0").text().split("/")[0])/parseFloat($("#GPSLongitude0").text().split("/")[1]) + parseFloat($("#GPSLongitude1").text().split("/")[0])/(parseFloat($("#GPSLongitude1").text().split("/")[1])*60) + parseFloat($("#GPSLongitude2").text().split("/")[0])/(parseFloat($("#GPSLongitude2").text().split("/")[1])*3600);
if($("#GPSLongitudeRef").text() == "W"){
imagelong = "-" + imagelong;
}

このようにして出てきた結果を、google.maps.LatLngメソッドの引数として与えてやれば良い。

ということで、EXIFViewerでの地図表示が可能になった。労力のわりに、いまや出来て当たり前の機能のように見えるからどうにも報われないのです。


PHP、JavaScriptそれぞれにおける無名関数

PHPとJavaScriptを同時に使って開発を行う事が多いという個人的事情のため、混同しないようにする目的で書いているこの比較シリーズですが、今回は無名関数について扱います。

無名関数というのは、関数的な処理のまとまりを、名前空間を消費することなくアドホックに定義する手法で、最近ではajaxなどの通信を伴う関数の、コールバック関数として使われるのが一番身近な使用と言えるかもしれません。無名関数使用例の紹介もかねて、PHPとJavaScriptそれぞれの無名関数の書式を見てみましょう。

PHPにおける無名関数

基本的な書式

以前、spl_autoload_register関数の説明で用いた例です。

//無名関数の登録
spl_autoload_register(function($classname){
include $classname . ".class.php";
});

なにやらjQuery方面でよく見かけそうな、中括弧と小括弧の連続です。
無名関数を使っているのはspl_autoload_register関数の引数の部分で、したがってfunctionの前の”(“と最後の”)”は、無名関数の書式とは関係ありません。つまり、通常の関数定義を関数名を抜かして行えば無名関数になるわけですね。

無名関数の即時実行

なお、定義した無名関数に値を与えて即時実行する場合、可変長引数を関数内で引き渡す記事の際に使ったcall_user_func_array関数、もしくはcall_user_func関数を使います。

//無名関数の即時実行
//call_user_func(関数名,引数)という書式でしたね。
 
call_user_func(function($classname){
include $classname . ".class.php";
},"dullFunction");

PHPの無名関数の注意点としては、無名関数が使用可能なヴァージョンが5.3以降と、比較的最近実装された機能だという事です。現在稼働中のプログラムでも、5.2系のヴァージョンで作成され今まで運用されているものが結構ありますので、その場合無名関数は使えないので注意です。

JavaScriptにおける無名関数

基本的な書式

JavaScriptを使ったajax開発では、無名関数が頻繁に登場します。以下はjQueryの$.ajaxでコールバックとして無名関数を登録する例です。

//ajaxコールバックとして無名関数の登録
$.ajax({
url : "test.html",
success : function(data){
$("#contents").append(data);
}
});

successのラベル以降が無名関数の使用になります。PHPのものと、変数に$が付くか付かないか程度の違いしかありません。

無名関数の即時実行

無名関数の即時実行について、JavaScriptは専用の関数を持っていません。かわりにより簡潔な書き方で即時実行をすることが出来るようになっています。

//無名関数の即時実行
(function(data){
$("#contents").append(data);
})(anydata);

通常の無名関数の宣言との違いは、関数名にあたる部分を括弧でくくり、また関数の実行なので最後にセミコロンをつけているだけです。

即時実行の利用頻度は低いかもしれませんが、ソースコードの中に急に出現した括弧付きの無名関数宣言に、当惑してしまわぬよう頭の片隅に入れておくとよろしいでしょう。


PHP、JavaScriptで可変長引数を、関数内で引き渡す

可変長引数が抱える心の闇

PHP、JavaScriptそれぞれの可変長引数サポートという記事で扱ったテクニック、可変長引数。関数定義でarg1,arg2,arg3…と続ける事無く、スマートに引数を取り出す事が出来ます。

しかしながら、引数にローカル変数を充てないという事は、関数内での引数の引き渡しで困難に出会います。

//PHP
function dullfunction(){
$args = func_get_args();
otherfunction($args);
}
 
//想定した動作をしてくれません。

動作が想定通りいかないのは、当たり前の事です。この書き方ですと、関数otherfunction()に配列$argsへの参照が与えられるだけで、したがってotherfunctionの中では$argsを参照した1つの引数が与えられたものと見なされてしまいます。

それならば、引数の一つ一つを$args[0],$args[1]…のように分けて渡せば良い!と思われるかもしれませんが、それは可変長引数という前提に忠実ではありません。

そこで、そもそも関数に引数として配列を渡せないのはおかしいと上司に叛旗を翻してみて下さい。数十社クビになった後、親切な上司が(もしいればですが)教えてくれます。

PHP、JavaScriptで配列を関数の引数として与える方法

PHP、JavaScriptともに、配列を要素分だけ引数として与える方法があります。

//PHP
function dullfunction(){
$args = func_get_args();
call_user_func_array("otherfunction", $args);
}

PHPでは、call_user_func_array()という関数を用います。第一引数には、関数の名前を指定します。このとき()はつけません。また、以下のように第一引数に無名関数をとる事も出来ます。

//PHP
function dullfunction(){
$args = func_get_args();
call_user_func_array(function(){$args2 = func_get_args(); print_r($args2);}, $args);
}
 
//無名関数の中でcall_user_func_array("dullfunction",$args2);とかやってはいけない

ところで、このcall_user_func_array()について、あちこちの説明でまるでユーザが定義した関数でないと動かないかのような誤解を与える表現をしていますが、別に普通の関数(var_dumpとか…)を与えても動いてくれます。便利ですよ。

お次はJavaScriptの場合です。

//JavaScript
function dullfunction(){
otherfunction.apply(this,arguments);
}

可変長だろうがそうでなかろうが、JavaScriptの引数がargumentsで取れるという事は既に説明しました。今回新しく登場したapplyというfunctionオブジェクトのメソッドは、メソッドを呼び出した関数の中の”this”キーワードに別の値を代入するという、火星人が作ったようなよくわからない働きをするメソッドですが、第二引数には配列またはオブジェクトで呼び出し元関数への引数を与える事が出来るようになっています。

//JavaScript
function.apply(引数1,引数2);
 
//引数1:関数内の"this"に充てるもの。ただ可変長引数を与えたいだけならthisを指定し
//この部分の働きを殺す。
//引数2:関数に与える引数

きっと親切に教えてくれる上司は火星人です。即刻辞表を突き付けましょう。


PHP、JavaScriptそれぞれの可変長引数サポート

PHP、JavaSciptの関数処理記述方法

PHPとJavaScriptでユーザ関数の定義をするとき、引数をもつ関数の場合には以下のような書式を用います。

//PHP
function dullfunction($arg1,$arg2){
return $arg1 + $arg2;
}
//JavaScript
function dullfunction(arg1,arg2){
return arg1 + arg2;
}

何の変哲も無い、プログラミング言語普遍の関数定義ですね。これは関数の引数が2つであると決まっている場合の書き方になります。arg1、arg2と書いているのが引数(argument)になります。

定義した関数の数より少ない引数を与える場合

上で例に挙げた関数では、そもそも処理内容的に引数が二つとも揃っていないとエラーになってしまいます。それならば、内部処理が2つの引数を必ずしも必要としない関数であれば、定義より少ない関数を与えても大丈夫なのか、という話ですが、PHPとJavaScriptの場合はそこら辺を柔軟に解決しています。

PHPの場合には、無視される可能性のある引数にデフォルト値を設定する事でエラーを抑えます。

//PHP
function dullfunction($arg1,$arg2=null){
return "ダルい";
}
 
dullfunction(); //これはエラー
dullfunction(100); //$arg1には100が与えられる。$arg2はnull。エラーにはならない。
dullfunction(100,200); //$arg1に100、$arg2に200が与えられる。エラーにはならない。

関数定義中の引数部分で代入が行えるPHPらしい(ある意味破天荒な)解決です。ただし、デフォルト値を与える引数は必ず与えない引数の後に書くようにしないといけません。

JavaScriptの場合、与えられた引数の数が関数定義より少なくても、全く問題がありません。

//JavaScript
function dullfunction(arg1,arg2){
return "ダルい";
}
 
dullfunction(); //問
dullfunction(100); //題
dullfunction(100,200); //ナシ

この辺が入門用言語としてポピュラーな理由かもしれません。WEB制作者の中には、JavaScriptは気付いたら使えるようになっていたという習得歴の方もいらっしゃるかもしれません。私もそうでした。

関数が受け付ける引数の数を無制限にしたいとき(可変長引数)

表題の通り、引数の数を無制限にしたい場合です。たとえば与えられた引数全ての和を求めるというのは、よくある処理です。

PHPの場合は、とりあえず引数を持たない関数として定義しておいて、関数内部から実際に与えられた引数にアクセスする関数を呼び出します。この、あらゆるものを関数として用意してしまうようなところがPHP的だと言えるのですが、まあそれはどうでも良い事ですね。

//PHP
function dullfunction(){
$sum = 0;
$num = func_num_args();
for($i=0;i<$num;$i++){
$sum += func_get_arg($i);
}
return $sum;
}
 
//もしくは…
function dullfunction(){
$sum = 0;
$arglist = func_get_args();
for($i=0;i<count($arglist);$i++){
$sum += $arglist[$i];
}
return $sum;
}
 
dullfunction(100,200...); //お好きな数どうぞ

func_num_args()という引数の数を返す関数と、func_get_arg()/func_get_args()という引数そのものをインデックス指定で/配列として 返す関数があります。

JavaScriptの場合、関数定義とは関数オブジェクトを作るということであり、関数オブジェクトには与えられた引数を自動的に格納するargumentsプロパティがあります。したがって、実は定義式での引数の数の宣言もへったくれも無いわけです。引数が何個来ようが、不真面目にargumentsプロパティにアクセスすればいいのです。

//JavaScript
function dullfunction(){
var sum = 0;
for(i=0;i<dullfunction.arguments.length;i++){
sum += dullfunction.arguments[i];
}
return sum;
}
 
dullfunction(100,200...); //お好きな数どうぞ

PHPもJavaScriptも、かなりゆるい言語であることがわかりましたね。そしてゆるさの性格の違いも、垣間見えたのではないかと思います。


JavaScriptがプロトタイプベース言語だということについて

オブジェクト指向言語も決して一枚岩でありません。西の横綱もいれば、西の高校生探偵もいます。このブログで扱う事の多いPHP、JavaScriptなどについて見てみますと、PHPはクラスベース言語というタイプに含まれるのに対して、JavaScriptはプロトタイプベース言語というタイプに含まれます。JavaScriptのプロトタイプベース言語の方が、幾分マイナーですので、こちらの特徴を追ってみましょう。

プロトタイプベース言語には、クラスの概念が無い

オブジェクト指向についてかじった事のある人間ならば、あらゆるものがオブジェクトで、そのオブジェクトには設計図たるクラスが必要で…といったようなオブジェクト指向のイロハに馴染んでいることと思います。一方JavaScriptが属するマイノリティ、プロトタイプベース言語には、オブジェクトの設計図たるクラスは存在しません。それでは、オブジェクトは何を元にして作られるのか、何を実体化してインスタンスをつくるのかという疑問が当然沸き上がる筈でしょう。

実は、JavaScriptにおけるインスタンス化は、既存のオブジェクトを丸々コピーすることによって行われます。コピー元もまた実体をもったオブジェクトなのです。

はい、メモリの無駄遣い発見!JavaScriptはとんだ低級言語だぜ!と思われた方は、オブジェクト指向の意義についてしっかりと理解された方だと思います。既存のオブジェクトをコピーするという方法では、もしプロパティやメソッドの実装部分が100個あったら、インスタンスそれぞれにつき100個を丸ごとコピーしなければなりません。それでは、毎度毎度新しいオブジェクトを一から作成しているのと何ら変わりません。そこで、プロトタイプ言語ではそうした問題を解決する方法として、プロトタイプオブジェクトという概念を用意しています。

JavaScriptでのクラス(仮)宣言

JavaScriptでクラスのようなもの、つまりインスタンスにコピーされるコピー元のオブジェクトを宣言する場合には、変数(クラス(仮)名)への関数リテラルの代入だけで済みます。

var Conan = function(){};

これで終わりです。右側に書かれた無名関数がコンストラクタになりますので、何かしらプロパティやメソッドを設けたい場合には、function(){}の中に書けば大丈夫です。

var Conan = function(powder){
this.powder = powder;
this.pero = function(){
if(this.powder == "青酸カリ"){
alert("これは…青酸カリ!");
} else {
alert("この料理を作ったのは誰だっ!");
}
};
};

これでクラス(仮)Conanにプロパティpowderとメソッドpero()ができました。インスタンスを作成する際に引数を与えることで、プロパティpowderを初期化できます。

var conan = new Conan("青酸カリ");
conan.pero();
 
//結果 これは…青酸カリ!

JavaScriptのプロトタイプ

さて、先ほど挙げた問題に戻ります。このままですと、Conanクラスのインスタンスをconan1,conan2,conan3…とどんどん作っていくことで、その都度powderプロパティとpero()メソッドの宣言が行われメモリの無駄になってしまいます。そこで、JavaScriptのクラス(仮)には暗黙のプロパティであるprototypeというものが用意されており、このプロパティ(正体はオブジェクト)への値の代入を行うことで、プロパティやメソッドの追加が行えるようになっています。

var Conan = function(powder){
this.powder = powder;
};
 
Conan.prototype.pero =  function(){
if(this.powder == "青酸カリ"){
alert("これは…青酸カリ!");
} else {
alert("この料理を作ったのは誰だっ!");
}
};
 
//宣言してもいないConanクラスのプロパティ prototype に値を入れてしまう

これで、先ほどと同じくインスタンスからメソッドを実行できるようになります。

どの辺りがメモリの節約になったのかと言うと、実はインスタンス側にはpero()メソッドがコピーされておらず、インスタンスからpero()メソッドが呼ばれると、インスタンスの持つprototypeオブジェクトへの参照を便りに、prototypeオブジェクト中の同名メソッドを呼ぶ仕組みになっているのです。
つまりどれだけプロトタイプ側にメソッドやプロパティを追加しても、インスタンス側にはプロトタイプへの参照しか残らないので、経済的だというわけですね。

いかがだったでしょうか。決してJavaScriptの属するプロトタイプベース言語が、クラスベース言語に劣った低級なものでないという事が分かったのではないでしょうか。実はプロトタイプベース言語はクラスベース言語へのアンチテーゼとして作られたという経緯があり、クラスベースのオブジェクト指向の弱点である、開発の段階が進むにつれて設計図であるクラスへの設計図としての要求が高くなってくる(→リファクタリングの必要性が出てくる)といったところをうまく解決して、機能の追加をアドホックに行えるという持ち味を醸しています。

ということで、JavaScriptは案外面白い奴です。


PHPとJavaScriptでURLのクエリストリングを評価する

クエリストリングというのは、ほうぼうのWEBサービスでよく見かける、URLの後ろについた情報を持った文字列です。たとえば、youtubeで動画の検索をするとき、URLの後ろに ?search_query=検索文字列 として、URLにユーザ検索の内容をつけて遷移後のページに渡しています。
このクエリストリングは、search_queryのような普遍的な語でなくても、勝手な語を選んでつけることができます。そもそも、HTMLでフォームを作って送信方法にGETを選ぶと、input要素のユーザが設定したname属性がストリングになっていますね。またinput要素が複数の場合は、&で結ばれています。これもクエリストリングの特徴です。

さて、このクエリストリングをプログラム側でキー値と値の組で取得するにはどうすればよいでしょうか。

PHPでクエリストリング

PHPの場合、大抵の文字列操作は標準の関数として装備されています。本当に。自分で作る前にまず探してみると、大抵便利なのがあります。再発明した車輪はゴミの日にそっと出しましょう。

1.URL文字列からクエリストリングを取り出す場合
//このようなURLがあったとき
$url = "http://akisi.tabiyaku.net/index.php?query1=値1&query2=値2";
 
//クエリ部分を抽出する関数
$query = parse_url($url,PHP_URL_QUERY);
//$queryは"query1=値1&query2=値2"
 
2.現在のページのURLからクエリストリングを取得する場合
$query = urldecode($_SERVER["QUERY_STRING"]);
//urldecodeで、%E3みたいなコードになっているのをデコードする
 
3.取得したクエリストリングをキーと値の組に
parse_str($query,$arr);
 
//$arr["query1"]は値1
//$arr["query2"]は値2

3で$arrを指定しなければ、通常の変数($query1,$query2)にそれぞれの値が入ります。

JavaScriptでクエリストリング

JavaScriptの場合には、車輪の開発者になる余地が大いにあります。何故PHPのような標準でクエリストリングを扱う関数が無いのでしょう?簡単な話です。JavaScriptは未来の言語だからです。未来の車には、車輪など必要ありません!

1.URL文字列からクエリストリングを取り出す場合
 
//このようなURLがあったとき
var url = "http://akisi.tabiyaku.net/index.php?query1=値1&query2=値2";
 
//クエリ部分を抽出する(処理的には、?を探して以降の文字列をとる)
var query = url.slice(url.indexOf("?")+1);
//queryは"query1=値1&query2=値2"
 
2.現在のページのURLからクエリストリングを取得する場合
var query = decodeURI(location.search);
//decodeURIで、%E3みたいなコードになっているのをデコードする
//ただし、先頭に?を含むところがPHPと異なるので、?なしで取得する場合
var query = decodeURI(location.search).substring(1);
 
3.取得したクエリストリングをキーと値の組に
var arr = new Array();
var qarr = query.split("&");
for(i in qarr){
arr[qarr[i].substr(0,qarr[i].indexOf("="))] = qarr[i].substr(qarr[i].indexOf("=")+1);
}
//arr["query1"]は値1
//arr["query2"]は値2

こういった具合です。

実はJavaScript自体の関数ラインナップはPHPより劣るものの、PHPと同様の充実度を誇るのがjQueryです。確実に世界のどこぞの誰かが、あなたより先に車輪を開発してプラグインにしています。たとえばクエリストリングの評価ならば、jquery.toObject.jsこちらを使えばよろしいでしょう。こんな何某のWEB制作日記とか言う場末ブログのブックマークは、今すぐゴミ箱に放り込みましょう。