![]()
![]()
![]()
JavaScriptについて
LastUpdate : 12/04/22
JavaScriptについて基本的なことをまとめています。
業務ではそれとなく、なんとなく書いていたのですが一度基本的なところからおさえてみようかなと思いJavaScriptについてまとめてみます。
ここにあるJavaScriptの動作確認はFirefox11で行っています。
勉強するにあたって『JavaScript本格入門』(技術評論社)を参考にしました。
もくじ
以下(1)(2)にて、ブラウザのことは考えず、プログラミング言語としてのソースコード記述方法についてサンプルを書いています(実行結果を確認できるようにできるようにhtmlから呼び出しを行っていますが)。
(3)にて、ブラウザ使ったサンプルを書いています。そして、(4)にてajaxのサンプルを書いています。
エディタでシコシコとソースを書いて、ブラウザで確認・・・というのが一番ベーシックな気がする。
各ブラウザにて、JavaScript開発支援用のプラグインがあります。Firefoxでは「Firebug」が有名っぽい。いろんなツールがあるので、調べてお好みのツールを使うのがよいかと。
JavaScriptのIDEとして、ちょっと調べたら以下のものがあるらしい(別にJavaScript専用ってわけではないがw)。
・Aptana Studio
・Visual Web Developer
ほか、もっとあるんでしょうが、子面倒なので放置。
私は個人的趣味の関係でAptanaStudioを使ってます。
.NETで開発するなら、MS謹製ツールVisualWebDeveloperを使った方が他ツールと連携が取れたりして便利なのかもしれません(使ったことないので知りませんがw)。
AptanaStudioは、Eclipseのプラグインとしても提供されてるっぽい。ダウンロードページで、Eclipseと抱き合わせでDLできる版と、プラグインだけ版が選択できるみたい。
好みの問題ですが、私は前者を使ってます。デフォルトでは英語ですが、Pleiadesを使えば日本語化できます。
JavaScriptでは、変数の宣言は不要です。
しかし、ソースがわかりやすくなり無用な混乱が避けられるので、常に宣言をしてから使うほうが良い。
また、変数に対する細かい型の概念はありません。代入された値を自動でJavaScriptが解釈してくれます。
JavaScriptで用意されている型は以下の通りです。
| number(数値型) | 整数も、小数もこの型で扱う。先頭に0をつけると8進数、0xをつけると16進数として値を代入できる。 |
| string(文字列型) | 文字列型"か'で囲んだものを文字列として扱う。 |
| boolean(真偽型) | true/falseを扱う。 |
| null | 未定義を表す定数。JavaScriptではnullはあまり使われない様子。undefinedがよく使われるらしい。 |
| undefined |
また、関数・オブジェクト・配列などを変数に代入すると、その変数は参照型となる。
| sample.js |
/*
* 基本的なJavaScriptの書き方(データ型・変数宣言)のサンプル
*/
// 変数の定義(グローバル変数)
var numInteger = 01+0x1+1; // 数値(8進数の1と16進数の1と10進数の1を合計した値)
var numFloating = -3.14; // 小数点を含む数値
var string = "うはww"; // 文字列
var torf = true; // 真偽値
var undef1 = null; // 未定義
var undef2 = undefined; // 未定義
// 関数
function func1() {
var numInteger = 1000; // ローカル変数として使用する
document.write("func1関数だよ!:"+numInteger);
document.write("<br>");
}
func1();
document.write("数値:"+numInteger+"<br>");
document.write("小数値:"+numFloating+"<br>");
document.write("文字列:"+string+"<br>");
document.write("真偽値:"+torf+"<br>");
document.write("undef1:"+undef1+"<br>");
document.write("undef2:"+undef2+"<br>");
// 以下の方法はあくまで例。Numberクラス/Stringクラスを使用してもよいかと。
var num1 = 123;
var num2 = 0;
document.write("数値を文字列として認識させる:"+(num1+""+num2)+"<br>");
var str1 = "123";
var str2 = "0";
document.write("文字列を数値として認識させる:"+(parseInt(str1)+parseInt(str2))+"<br>"); |
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>基本的なJavaScriptの書き方(変数と型)</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html> |
| 実行結果 |
func1関数だよ!:1000 |
JavaScriptの組み込み関数・変数は以下のものがあるらしい。
■変数
| 変数名 | 説明 |
| NaN | 数値ではない値 |
| Infinity | 無限(∞) |
| undefined | 未定義 |
■関数
| 関数名 | 説明 |
| isFinite(num) | 有限値であるかどうかのチェックを行う |
| isNaN(num) | NaNであるかをチェックする |
| Boolean(val) | 真偽値に変換 |
| Number(val) | 数値型に変換 |
| String(val) | 文字列型に変換 |
| parseFloat(str) | 文字列を浮動小数点に変換 |
| parseInt(str) | 文字列を整数値に変換 |
| escape(str) | 文字列をエスケープ処理する |
| unescape(str) | エスケープ処理された文字列を元に戻す |
| encodeURI | 文字列をURIエンコードする |
| decodeURI | 文字列をURIデコードする |
| encodeURIComponent(str) | 文字列をURIエンコードする |
| decodeURIComponent(str) | 文字列をURIデコードする |
| eval(exp) | 式・値を評価する |
eval関数は他の言語でもよく言われますが扱い注意です。特別な用途以外使用しません。
グローバル変数・関数を使用した例を以下に示します。
| sample.js |
/*
* Javascriptに組み込まれている変数・関数についてのサンプル
*/
var val1 = NaN; // 非数
var val2 = Infinity; // 無限
var val3 = undefined; // 未定義
var val4 = "1"; // 数値ではない値
document.writeln("■isFinite関数<br>");
if (isFinite(val1))document.writeln("val1は非数ではありません<br>");
else document.writeln("val1は非数です<br>");
if (isFinite(val2))document.writeln("val2は非数ではありません<br>");
else document.writeln("val2は非数です<br>");
if (isFinite(val3))document.writeln("val3は非数ではありません<br>");
else document.writeln("val3は非数です<br>");
if (isFinite(val4))document.writeln("val4は非数ではありません<br>");
else document.writeln("val4は非数です<br>");
document.writeln("<br>■isNaN関数<br>");
if (isNaN(val1))document.writeln("val1は数値ではありません<br>");
else document.writeln("val1は数値です<br>");
if (isNaN(val2))document.writeln("val2は数値ではありません<br>");
else document.writeln("val2は数値です<br>");
if (isNaN(val3))document.writeln("val3は数値ではありません<br>");
else document.writeln("val3は数値です<br>");
if (isNaN(val4))document.writeln("val4は数値ではありません<br>");
else document.writeln("val4は数値です<br>");
document.writeln("<br>■escape/unescape関数<br>");
var msg = "abcdefあいうえお";
var msgEscape = escape(msg);
var msgUnescape = unescape(msgEscape);
document.writeln("escape("+msg+") : "+msgEscape+"<br>");
document.writeln("unescape("+msgEscape+") : "+msgUnescape+"<br>");
document.writeln("<br>■encodeURI/decodeURI/encodeURIComponent/decodeURIComponent関数<br>");
var url = "http://alctail.sakura.ne.jp/dummy?msg=うはw";
var urlEncode = encodeURI(url);
var urlDecode = decodeURI(urlEncode);
var urlEncodeComponent = encodeURIComponent(url);
var urlDecodeURIComponent = decodeURIComponent(urlEncodeComponent);
document.writeln("encodeURI("+url+") : "+urlEncode+"<br>");
document.writeln("decodeURIe("+urlEncode+") : "+urlDecode+"<br>");
document.writeln("encodeURIComponent("+url+") : "+urlEncodeComponent+"<br>");
document.writeln("decodeURIComponent("+urlEncodeComponent+") : "+urlDecodeURIComponent+"<br>");
document.writeln("<br>■eval関数<br>");
function f(msg){ document.writeln(msg+"<br>"); }
var exp = "f('nya')";
eval(exp);
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Javascriptに組み込まれている変数・関数について</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
■isFinite関数 val1は非数です val2は非数です val3は非数です val4は非数ではありません ■isNaN関数 val1は数値ではありません val2は数値です val3は数値ではありません val4は数値です ■escape/unescape関数 escape(abcdefあいうえお) : abcdef%u3042%u3044%u3046%u3048%u304A unescape(abcdef%u3042%u3044%u3046%u3048%u304A) : abcdefあいうえお ■encodeURI/decodeURI/encodeURIComponent/decodeURIComponent関数 encodeURI(http://alctail.sakura.ne.jp/dummy?msg=うはw) : http://alctail.sakura.ne.jp/dummy?msg=%E3%81%86%E3%81%AF%EF%BD%97 decodeURIe(http://alctail.sakura.ne.jp/dummy?msg=%E3%81%86%E3%81%AF%EF%BD%97) : http://alctail.sakura.ne.jp/dummy?msg=うはw encodeURIComponent(http://alctail.sakura.ne.jp/dummy?msg=うはw) : http%3A%2F%2Falctail.sakura.ne.jp%2Fdummy%3Fmsg%3D%E3%81%86%E3%81%AF%EF%BD%97 decodeURIComponent(http%3A%2F%2Falctail.sakura.ne.jp%2Fdummy%3Fmsg%3D%E3%81%86%E3%81%AF%EF%BD%97) : http://alctail.sakura.ne.jp/dummy?msg=うはw ■eval関数 nya |
他の言語であるような制御構文は一通りそろってます。
以下の制御構文の例をサンプルコードに示します。
・if
・switch
・while
・do-while
・for
・contine
・break
・ラベル付きbreak
・例外処理
特別ほかの言語にないようなものがあるわけではありません。書き方も同じです(スクリプト言語で例外処理があるのがちょっとめずらしい??)。
ちなみに、例外処理は処理的に時間がかかるので、使いどころ注意らしい(よくある話ですが、forループ中にtry-catchを書くのではなく、forループをtry-catchでくくるようにするなど・・・)。
| sample.js |
/*
* 制御構文のサンプル
*/
// if文による分岐
document.writeln("<br>■if文のサンプル<br>");
var src = 10;
var dest = 200;
if ( src > dest ) {
document.writeln("srcはdestより大きいです<br>");
} else if (src == dest) {
document.writeln("srcとdestは同じ値です<br>");
} else {
document.writeln("srcはdestより小さいです<br>");
}
// switch文による分岐
document.writeln("<br>■switch文のサンプル<br>");
var currentMonth = new Date().getMonth(); // 現在の月を取得する(0を起点にしている)
switch(currentMonth) {
case 0: /* fall through */
case 1: /* fall through */
case 2:
document.writeln("1月〜3月はまだ寒いです。<br>");
break;
case 3:
document.writeln("4月は桜が咲き、5月は五月病時期です。<br>");
break;
case 4: /* fall through */
case 5: /* fall through */
case 6: /* fall through */
case 7: /* fall through */
case 8:
document.writeln("梅雨から夏の時期です。冷房をつけっ放しの時期です、。<br>");
break;
case 9: /* fall through */
case 10:/* fall through */
case 11:
document.writeln("冬です。<br>");
break;
default:
document.writeln("12月より大きい月を返すカレンダーはこの世にないはずですよ☆<br>");
}
// while文
document.writeln("<br>■while文のサンプル<br>");
var counter = 5;
while (counter--) {
document.writeln(" while counter:"+counter+"<br>");
}
// do-while文
document.writeln("<br>■do-while文のサンプル<br>");
var counter = 5;
do {
document.writeln(" do-while counter:"+counter+"<br>");
}while (counter--)
// for文のサンプル
document.writeln("<br>■for文のサンプル<br>");
for (var cnt=0; cnt<5; cnt++) {
document.writeln(" while counter:"+cnt+"<br>");
}
// for-in文のサンプル
document.writeln("<br>■for-in文のサンプル<br>");
var arr = ["1番目", "2番目", "3番目"];
for (var val in arr) {
document.writeln(" "+val+"<br>");
}
// (おまけ)breakとcontinueの使い方
var count = 0;
breakLabel:
for( ; ; count++) {
if (count < 5) {
continue; // 以降の処理を中断し、ループの先頭に戻る
}
for(var i=count;;i++) {
if (count == 0) {
break; // 通常のbreak(現在処理中のループを抜ける)
}
if(i > 5) {
break breakLabel; // どのループから抜けるのかラベルを指定
}
}
}
// 例外処理のサンプル
document.writeln("<br>■例外処理<br>");
try {
//var = 1/0みたいなゼロによる除算で発生する例外や以下のように自分で起こすことも可能
throw new Error("例外ですよ☆");
} catch (e) {
document.writeln(" 例外発生:"+e.message+"<br>");
} finally {
document.writeln(" 例外処理のfinally処理<br>");
}
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>制御構文</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
■if文のサンプル srcはdestより小さいです ■switch文のサンプル 1月〜3月はまだ寒いです。 ■while文のサンプル while counter:4 while counter:3 while counter:2 while counter:1 while counter:0 ■do-while文のサンプル do-while counter:5 do-while counter:4 do-while counter:3 do-while counter:2 do-while counter:1 do-while counter:0 ■for文のサンプル while counter:0 while counter:1 while counter:2 while counter:3 while counter:4 ■for-in文のサンプル 0 1 2 ■例外処理 例外発生:例外ですよ☆ 例外処理のfinally処理 |
関数内でvarで定義した変数はローカル変数となります。
ローカル変数の挙動が、他のメジャーな言語(C/C++やJava)と動作が異なる点があります。
ローカル変数は、関数内どこにでも定義可能ですが、定義されると、それはそのスコープ内(関数内)すべてで有効になります。
関数内の一番下に記述しても、関数の一番上の処理から、その変数はローカル変数として定義されていることになります。
例示した例ですと、func関数内で、valという変数が定義された状態となります。関数の上部の部分のコードでは、定義はされているが値は代入されていない状態です。
結果、undefinedが出力されます。
| sample.js |
/*
* 変数スコープで気をつけたいところ
*/
var val = "ゆがみねぇな"; // グローバル変数
function func() {
document.writeln("result : "+val);
var val = "つい最近は・・・岩に隠れとったのか?";
}
// 関数を呼び出す
func();
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>変数スコープで気をつけたいところ</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
result : undefined |
C言語などと基本的に同じです。算術演算子・比較演算子・ビット演算子・論理演算子・3項演算子はC言語と同じような感覚でつかえる様です。なので、例としては省略しました。
C言語にない要素として、instanceofやdelete、typeofがありますが、こちらはクラスについて説明する際扱います。
C言語とはちょっと違うかな?と思ったところの例を以下のソースに挙げてみました。
==は左右のオブジェクトを値に変換し、評価をする演算子みたいです。まぁ、値に変換する方法はなんなのか不明ですが・・・(ここは調べきれなかった)。文字列と数値なんかは値が同じであれば等しいと評価してくれます。
===は左右のオブジェクトのアドレスが等しいか等しくないかを比較するだけみたいです。
| sample.js |
/*
* 演算子について
*/
document.writeln("<br>■比較演算子(==と===)<br>");
var cmpVal1 = 1; // 数値
var cmpVal2 = "1"; // 文字列
// ==で比較
if (cmpVal1 == cmpVal2)document.writeln("cmpVal1とcmpVal2は等しいです(==で比較)<br>");
else document.writeln("cmpVal1とcmpVal2は等しくないです(==で比較)<br>");
// ===で比較
if (cmpVal1 === cmpVal2)document.writeln("cmpVal1とcmpVal2は等しいです(===で比較)<br>");
else document.writeln("cmpVal1とcmpVal2は等しくないです(===で比較)<br>");
document.writeln("<br>■シフト演算子(>>と>>>の違い)<br>");
var num1 = 8;
var num2 = 8;
num1 >>>= 1;
num2 >>= 1;
document.writeln("(正の値)シフト演算子(>>>) : "+num1+"<br>");
document.writeln("(正の値)シフト演算子(>>) : "+num2+"<br>");
num1 = -8;
num2 = -8;
num1 >>>= 1;
num2 >>= 1;
document.writeln("(負の値)シフト演算子(>>>) : "+num1+"<br>");
document.writeln("(負の値)シフト演算子(>>) : "+num2+"<br>");
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>演算子について</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
■比較演算子(==と===) cmpVal1とcmpVal2は等しいです(==で比較) cmpVal1とcmpVal2は等しくないです(===で比較) ■シフト演算子(>>と>>>の違い) (正の値)シフト演算子(>>>) : 4 (正の値)シフト演算子(>>) : 4 (負の値)シフト演算子(>>>) : 2147483644 (負の値)シフト演算子(>>) : -4 |
functionというキーワードを使い、関数を記述します。
また、Functionオブジェクトを使用する方法もありますが、通常は使用しないと思います・・・。
関数定義の仮引数の数と、呼び出しを行う際の引数の数が不一致でも、関数呼び出しが可能です。指定されていない値については、undefinedになります。
また、関数内では、暗黙のオブジェクトargumentsを使用可能です。関数が呼び出された際の、引数にまつわる情報が格納されています。
サンプルでは、関数の定義方法(通常の関数定義と、Functionクラスを使った方法)、再起呼び出し、関数内関数、クロージャについて例を書いています。
| sample.js |
/*
* 関数について
*/
// 関数の定義
function sampleFunction(arg1, arg2) {
//argumentsは関数内で暗黙に定義されるArgumentsオブジェクト。引数についての情報を保持している。
for (var i=0; i<arguments.length; i++) {
document.writeln("引数["+i+"] : "+arguments[i]+"<br>");
}
document.writeln("arg1 : "+arg1+"<br>");
document.writeln("arg2 : "+arg2+"<br>");
}
// 関数を呼び出す
sampleFunction("にゃんにゃん");
// Functionオブジェクトを使用する
var args = "num1, num2";
var funcSrc = "return num1*num2;";
var funcVal = new Function(args, funcSrc);
// 関数を呼び出す
document.writeln("Functionオブジェクトを使用した例 : "+funcVal(10, 20)+"<br>");
// 再起呼び出しの関数
function sampleRecursiveFunction(num) {
if (num < 1) {
return num;
}
//return num + sampleRecursiveFunction(num-1); という記述でもよい。
return num + arguments.callee(num-1);
}
// 関数を呼び出す
document.writeln("再帰呼び出し : "+sampleRecursiveFunction(10)+"<br>");
// JavaScriptでは、関数内関数も作ることが可能です
function outerFunction() {
document.writeln("outerFunctionが呼び出されました!<br>");
document.writeln("innderFunctionの結果 : "+innderFunction(100, 100)+"<br>");
function innderFunction(num1, num2) {
return num1 + num2;
}
}
document.writeln("関数内関数を使用したサンプル : "+outerFunction()+"<br>");
// クロージャもあるよ!
function closureFunction(dateStrYYYYMMDD) {
var date = new Date(dateStrYYYYMMDD); // この値が呼び出される毎にメモリに保存されていく(クロージャ)。
return function() {
// 1日進めたものを返す
date.setDate(date.getDate()+1);
return date;
}
}
// closureFunction呼び出す
var date1 = closureFunction("2012/4/1");
var date2 = closureFunction("2012/5/1");
var date3 = closureFunction("2012/6/1");
document.writeln("クロージャのサンプル(date1) : "+date1()+"<br>");
document.writeln("クロージャのサンプル(date1) : "+date1()+"<br>");
document.writeln("クロージャのサンプル(date2) : "+date2()+"<br>");
document.writeln("クロージャのサンプル(date2) : "+date2()+"<br>");
document.writeln("クロージャのサンプル(date3) : "+date3()+"<br>");
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>関数について</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
引数[0] : にゃんにゃん arg1 : にゃんにゃん arg2 : undefined Functionオブジェクトを使用した例 : 200 再帰呼び出し : 55 outerFunctionが呼び出されました! innderFunctionの結果 : 200 関数内関数を使用したサンプル : undefined クロージャのサンプル(date1) : Mon Apr 02 2012 00:00:00 GMT+0900 クロージャのサンプル(date1) : Tue Apr 03 2012 00:00:00 GMT+0900 クロージャのサンプル(date2) : Wed May 02 2012 00:00:00 GMT+0900 クロージャのサンプル(date2) : Thu May 03 2012 00:00:00 GMT+0900 クロージャのサンプル(date3) : Sat Jun 02 2012 00:00:00 GMT+0900 |
JavaScriptの言語上の組み込みクラス(オブジェクト)として、以下のものがあるらしい。
| クラス名 | 説明 |
| Object | すべてのオブジェクトのひな形(上位クラス) |
| String | 文字列型のオブジェクト |
| Boolean | 真偽値型のオブジェクト |
| Number | 数値型のオブジェクト |
| Function | 関数型のオブジェクト |
| Date | 日付型のオブジェクト |
| Error | 例外処理にてエラー情報を格納するオブジェクト |
| Array | 配列操作機能を提供するオブジェクト |
| Math | 数値演算機能を提供する機能 |
| RegExp | 正規表現の機能を提供するオブジェクト |
型のラッパークラスと、その他、配列操作、数値演算、正規表現がある。それぞれの、クラス内に、メソッドが提供されている。
クラスに属するstaticな関数と、そうではない(クラスのインスタンスがないと使えない)関数があります。
それぞれのクラスに定義されている関数については、リファレンスを参照してください。
ここでは簡単に、いくつかのオブジェクトの使い方を例示します。
| sample.js |
/*
* クラスについて(定義済みのクラス)
*/
// Stringクラスの使用例
document.writeln("<br>■Stringクラスの使用例<br>");
var str = "おいたんだれ";
document.writeln(str+"の文字の長さ : "+str.length+"<br>");
document.writeln("4文字だけ切り出す : "+str.substr(0, 4)+"<br>");
// Mathクラスの使用例
document.writeln("<br>■Mathクラスの使用例<br>");
var num = -10;
document.writeln(num+"の絶対値 : "+Math.abs(num)+"<br>");
document.writeln("ランダムな数値 : "+Math.random()+"<br>");
// Arrayクラスの使用例
document.writeln("<br>■Arrayクラスの使用例<br>");
// var arr = new Array("a", "b", "c")と記述するが、普通使わない。
var arrEmpty = []; // これで、空の配列を定義した状態。
var arr = ["a", "b", "c"]; // 3要素ある配列を作成
arr.push("d"); // 末尾に要素を追加
arr.unshift("☆"); // 先頭に要素を追加
for(var i=0; arr.length>0 ;i++) {
// 配列の先頭の要素を取得し、配列から削除していく
document.writeln(" ["+i+"] : "+arr.shift()+"<br>");
}
// Dateクラスの使用例
document.writeln("<br>■Dateクラスの使用例<br>");
var currentDate = new Date(); // 現在日付のDateクラスを作成
var specifyDate = new Date("2011/3/25"); // 指定の日付でDateクラスを作成する例
document.writeln("currentDate : "+currentDate+"<br>");
document.writeln("specifyDate : "+specifyDate+"<br>");
var year = currentDate.getFullYear(); // 年を取得
var month = currentDate.getMonth(); // 月を取得
var day = currentDate.getDay(); // 日を取得
var hour = currentDate.getHours(); // 時を取得
var minutes = currentDate.getMinutes(); // 分を取得
var sec = currentDate.getSeconds(); // 秒を取得
document.writeln(year+"/"+month+"/"+day+" "+hour+":"+minutes+":"+sec+"<br>");
// RegExpクラスの使用例
/*
* オプションの意味
* g : 文字列の最後までマッチングを行う
* i : 大文字・小文字を区別する
* m : 複数行
*/
document.writeln("<br>■RegExpクラスの使用例<br>");
//var reg = new RegExp("わんわん(.*?)/にゃんにゃん*.", "gi"); この記述を以下のように記述することも可能
var reg = /わんわん(.*?)\/にゃんにゃん*./i;
var regResult = "わんわんお/にゃんにゃんお".match(reg);
for(var i=0; i<regResult.length; i++) {
document.writeln(" 結果 : "+regResult[i]+"<br>");
}
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>オブジェクトについて(定義済みのオブジェクト)</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
■Stringクラスの使用例 おいたんだれの文字の長さ : 6 4文字だけ切り出す : おいたん ■Mathクラスの使用例 -10の絶対値 : 10 ランダムな数値 : 0.7187245287056016 ■Arrayクラスの使用例 [0] : ☆ [1] : a [2] : b [3] : c [4] : d ■Dateクラスの使用例 currentDate : Sun Mar 25 2012 17:18:17 GMT+0900 specifyDate : Fri Mar 25 2011 00:00:00 GMT+0900 2012/2/0 17:18:17 ■RegExpクラスの使用例 結果 : わんわんお/にゃんにゃんお 結果 : お |
JavaScript上のクラスは「プロトタイプベースのオブジェクト指向」ってやつです。このページでクラスと呼んでたものは、C++やJavaで言うクラスとは、仕組みが異なります。
このページでは「クラス」という言葉をたくさん使いましたが、正確には「クラス」は間違いで、「プロトタイプ」と書いた方が良い。だが、面倒なのでクラスと表記してます。言葉的になんだかしっくりこないので。
クラスの記述方法は独特なため、すこし説明を行います。
(1) クラスの定義・インスタンスの生成について
フィールド・メソッドを保持しない中身が空のクラスは以下の記述で出来ます。
var SampleClass = function() {};
なんでfunctionやねん。と思いますがそういうものだから受け入れるしかありません。
これでSampleClassというクラスが定義されたことになります。そして、このクラスを使うには以下のように、インスタンスの作成を行います。
var obj = new SampleClass();
これで、SampleClassのインスタンスへの参照がobjに代入された状態です。こんな感じでクラスを定義して、インスタンスの作成を行います。
(2) クラスにフィールド・メソッドの定義
フィールドの定義・メソッドの定義は以下のように行います。
var SampleClass = function(month, day) { this.month = month; this.day = day; this.dateStr = month+"/"+day; this.getMonth = function() { return this.month; } this.getDay = function() { return this.day; } this.getDateStr = function() { return this.dateStr; } };
上記の記述で、以下のフィールド・メソッドを保持するクラスSampleClassを定義したこととなります。
■フィールド
month
day
dateStr
■メソッド
getMonth
getDay
getDateStr
(上記記述方法でも間違いではないですが、他の書き方を通常はします。それについては、クラスの独自定義その2(クラスの作成方法)にて扱います)
this.XXXXと記述することで、クラスのフィールド/メソッドとなります。代入するものが、値ならフィールドに、無名関数ならメソッドとなります。functionの引数は、newする際の引数となります。
上記のクラスはいかのように、インスタンス化します。
var obj = new SampleClass(10, 1); // インスタンスを作成 var result = obj.getDateStr(); // メソッドを実行
また、作成したインスタンスに対し、フィールドやメソッドの追加も可能です(インスタンスに対して追加するのであって、クラス定義に追加するわけではないです。なので、別箇SampleClassのインスタンスをnewしても、そのインスタンスの中には、追加したフィールド/メソッドは存在しません。)
var obj = new SampleClass(10, 1); obj.year = '2011'; obj.getYear = function() { return this.year; } var year = obj.getYear();
クラスを定義し、インスタンスを生成する例を示します。
例の中では、クラスに、フィールドを定義する方法、メソッドを定義する方法、静的フィールドを定義する方法、静的メソッドを定義する方法を扱っています。
クラスに対し、prototypeというフィールドにメソッドを設定しています。このprototypeというのが、「プロトタイプベースのオブジェクト指向」というのにかかわってきます。
prototypeというフィールドは、クラスに自動的に用意されるフィールドです。
詳細に説明は行いませんが、とりあえずは、クラスにメソッドを定義する際は、prototypeに行うと考えていればいいと思います。
動作上の、クラスの独自定義その1(クラスについての説明)の例との差異として、メソッドがコンストラクタ(SampleClassに値を代入している無名関数のことを指します)内で定義していますが、このような記述の場合、インスタンスを作成する度に、これらのメソッドについても、そのインスタンスが保持することとなります(インスタンス毎にメソッド定義がコピーされる)。
つまり、ロジックがコピーされることになります。これはメモリの無駄な部分です。prototypeでメソッドの定義を行うようにすれば、インスタンス作成時にメソッドのコピーは行われません。メソッドの呼び出し時は、インスタンス内にあるメソッドをさがし、なければprototypeにて定義されているメソッドを探しに行き、みつかったらそれを呼び出す・・・という動作となります。
この自インスタンスになかったら、prototypeに探しに行く・・・という動作ですが、実行時点のprototypeの値に対して検索を行います。何が言いたいかと申しますと、インスタンス生成時に、prototypeにないメソッドでも、メソッド呼び出しを行った時点でprototypeにメソッドが存在すれば、呼び出し可能です。こういった動作はC++やJavaと異なるため覚えておく必要があります。
| sample.js |
/*
* クラスの独自定義その2(クラスの作成方法)
*/
var SampleClass = function(month, day) {
this.month = month;
this.day = day;
};
// クラスを継承する場合、以下の書き方をする
// SampleClass.prototype.getMonth = function() {
// return this.month;
// };
// SampleClass.prototype.getDay = function() {
// return this.day;
// };
// SampleClass.prototype.getDate = function() {
// return this.month+"/"+this.day;
// }
// 以下のような記述は、上記のものと同等。クラスを継承しないことがわかっている場合以下の形式でも良い。
SampleClass.prototype = {
getMonth : function() { return this.month; },
getDay : function() { return this.day; },
getDate : function() { return this.month+"/"+this.day; },
};
// SampleClassに対して静的フィールドを追加する
SampleClass.msg = "javaで言うstaticなフィールドです";
// SampleClassに対して静的メソッドを追加する
SampleClass.getMessage = function() { return "javaで言うstaticなメソッドです"; }
var obj = new SampleClass(10, 1);
document.writeln("日付:"+obj.getDate()+"<br>");
// 静的フィールドの値を取得する
document.writeln("SampleClass#msg : "+SampleClass.msg+"<br>");
// 静的メソッドを呼び出す
document.writeln("SampleClass#getMessage : "+SampleClass.getMessage()+"<br>");
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>クラスの独自定義その2(クラスの作成方法)</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
日付:10/1 SampleClass#msg : javaで言うstaticなフィールドです SampleClass#getMessage : javaで言うstaticなメソッドです |
prototypeとインスタンスの関係について、補足します。
prototypeには、メソッドのほか、フィールドの設定することが可能です。
prototypeが保持するフィールドと同名のフィールドをインスタンスに追加した場合、そして、prototypeのフィールドを変更した場合の、2ケースについてここでは説明します。
prototypeが保持するフィールドと同名のフィールドをインスタンスに追加した場合についてですが、隠蔽が行われ、インスタンスが保持するフィールドを常にアクセスするようになります。
prototypeのフィールドを変更した場合は、prototypeにあるフィールドが、インスタンス内にコピーされ、そのコピーされた値が変更されます。これは、同名のフィールドをインスタンスに追加した時と同じ挙動となります。
以下、例を示します。
| sample.js |
/*
* クラスの独自定義その3(隠蔽)
*/
var SampleClass = function() {
};
SampleClass.prototype = {
month : undefined,
day : undefined,
getMonth : function() { return this.month; },
getDay : function() { return this.day; },
getDate : function() { return this.month+"/"+this.day; },
};
var obj1 = new SampleClass();
document.writeln("obj1->month : "+obj1.getMonth()+"<br>"); // この時点ではprototypeのフィールドを取得している
obj1.month = 10;
document.writeln("obj1->month : "+obj1.getMonth()+"<br>"); // この時点では、obj1にあるフィールドを取得している
var obj2 = new SampleClass();
document.writeln("obj2->month : "+obj2.getMonth()+"<br>"); // prototypeのフィールドが変更されていないか確認
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>クラスの独自定義その3(隠蔽)</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
obj1->month : undefined obj1->month : 10 obj2->month : undefined |
クラスの継承も可能です。
以下に例を示します。
| sample.js |
/*
* クラスの独自定義その4(継承)
*/
// 上位クラスの作成
var ClassBase = function() {
};
ClassBase.prototype = {
getMsg : function() { return "妖精哲学の三信"; }
};
// SampleBaseクラスを継承したクラスを作成する
var SampleClass = function() {
}
SampleClass.prototype = new ClassBase(); // 上位クラスにClassBaseを設定する
SampleClass.prototype.getUnderMsg = function() {
return this.getMsg();
}
var obj = new SampleClass();
document.writeln(""+obj.getUnderMsg());
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>クラスの独自定義その4(継承)</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
妖精哲学の三信 |
継承関係は、インスタンスの作成時(newする時)で決定されます。そして、newした時点でインスタンスの継承関係がfixされます。
クラスの上位クラスを変更しても、作成済みインスタンスには影響ありません(インスタンス作成時の情報が保持されている)。これがちょっとややこしいところかなと思い、サンプルを作成しました。まぁ、ややこしい話です。
| sample.js |
/*
* クラスの独自定義その5(継承の補足)
* インスタンスを作成した時点の情報で、インスタンスの継承関係が決定し、それが保持される。
* 上位クラスを変更しても、作成済みのインスタンスには影響を及ぼさない例。
*/
// クラスを作成
var ClassBase = function() {
};
ClassBase.prototype = {
getMsg : function() { return "妖精哲学の三信"; }
};
// クラスを作成
var OtherClassBase = function() {
};
OtherClassBase.prototype = {
getMsg : function() { return "ゆがみねぇな"; }
};
// SampleBaseクラスを継承したクラスを作成する
var SampleClass = function() {
}
SampleClass.prototype = new ClassBase(); // 上位クラスにClassBaseを設定する
SampleClass.prototype.getUnderMsg = function() {
return this.getMsg();
}
var obj1 = new SampleClass(); // インスタンス生成
SampleClass.prototype = new OtherClassBase(); // 上位クラスにClassBaseを設定する
SampleClass.prototype.getUnderMsg = function() {
return this.getMsg();
}
var obj2 = new SampleClass(); // インスタンス生成
document.writeln("obj1 : "+obj1.getUnderMsg()+"<br>");
document.writeln("obj2 : "+obj2.getUnderMsg()+"<br>");
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>クラスの独自定義その5(継承の補足)</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
obj1 : 妖精哲学の三信 obj2 : ゆがみねぇな |
親、子供、孫、と、孫から見ると、上位クラスが2個(親とと子供)ある継承パターンについてです。
まず、例を以下に示します。
| sample.js |
/*
* クラスの独自定義その6(継承の補足その2)
*/
function showObject(obj) {
document.writeln("■objの内容表示<br>");
for (var v in obj) {
document.writeln("obj : "+v+"<br>");
}
}
// ■「YearClass」の定義
var YearClass = function(year) {
document.writeln("YearClassのコンストラクタが呼ばれたよ!<br>");
this.year = year;
}
YearClass.prototype = {
getClassName : function() { return "YearClass"; },
setYear : function(year) { this.year = year; }
};
// ■「YearMonthClass」の定義
var YearMonthClass = function() {
document.writeln("YearMonthClassのコンストラクタが呼ばれたよ!<br>");
this.month = undefined;
};
YearMonthClass.prototype = new YearClass();
YearMonthClass.prototype.getClassName = function() { return "YearMonthClass"; };
YearMonthClass.prototype.setMonth = function(month) { this.month = month; }
YearMonthClass.prototype.getYearMonth = function() { return this.year+"/"+this.month; };
// ■「YearMonthDayClass」の定義
var YearMonthDayClass = function() {
document.writeln("YearMonthDayClassのコンストラクタが呼ばれたよ!<br>");
this.day = undefined;
};
YearMonthDayClass.prototype = new YearMonthClass();
YearMonthDayClass.prototype.getClassName = function() { return "YearMonthDayClass"; }
YearMonthDayClass.prototype.setDay = function(day) { this.day = day; };
YearMonthDayClass.prototype.getDate = function() { return this.year+"/"+this.month+"/"+this.day; };
// インスタンスを作成する
var obj = new YearMonthDayClass();
obj.setYear(2010);
obj.setMonth(10);
obj.setDay(1);
document.writeln("result : "+obj.getDate()+"<br>");
document.writeln("className : "+obj.getClassName()+"<br>");
showObject(obj);
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>クラスの独自定義その6(継承の補足その2)</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
YearClassのコンストラクタが呼ばれたよ! YearMonthClassのコンストラクタが呼ばれたよ! YearMonthDayClassのコンストラクタが呼ばれたよ! result : 2010/10/1 className : YearMonthDayClass ■objの内容表示 obj : day obj : year obj : month obj : getClassName obj : setDay obj : getDate obj : setMonth obj : getYearMonth obj : setYear |
上記サンプルには、メソッドのオーバーライドも含まれています(getClassName関数)。
これを、getClassName関数内で、上位クラスのgetClassName関数を呼び出しを行いたい場合、工夫が必要となります(普通にsuper.getClassNameとかじゃダメなんです!!)
継承を行う処理を関数化し、そこで、任意のフィールド(ここでは__upperとした)に上位クラスの情報を保持させておき、実際、クラスの処理を記述する際は、そのフィールドを参照する・・・という風にしました。その役目をになっているのが、以下のサンプルでは、extendという関数です。ちなみに、__upperという変数名はてきとーに考えました。好きな名前でOKです。
以下の方法だと、上位クラスのコンストラクタが自動的に呼ばれなくなってしまうので、手動で呼び出しを行っています。
以下がその例です。
| sample.js |
/*
* クラスの独自定義その6(継承の補足その2)
*/
function extend(up, obj) {
var F = function(){ };
F.prototype = up.prototype;
obj.prototype = new F();
obj.prototype.__upper = up.prototype; // 何か適当なフィールドをつくって情報を保持させておきます。
obj.prototype.__upper.constructor = up; // こちらも上記と同様
obj.prototype.constructor = obj;
return obj;
}
function showObject(obj) {
document.writeln("■objの内容表示<br>");
for (var v in obj) {
document.writeln("obj : "+v+"<br>");
}
}
// ■「YearClass」の定義
var YearClass = function(year) {
document.writeln("YearClassのコンストラクタが呼ばれたよ!<br>");
this.year = year;
}
YearClass.prototype = {
getClassName : function() { return "YearClass"; },
setYear : function(year) { this.year = year; }
};
// ■「YearMonthClass」の定義
var _YearMonthClass = function() {
this.__upper.constructor();
document.writeln("YearMonthClassのコンストラクタが呼ばれたよ!<br>");
this.month = undefined;
};
var YearMonthClass = extend(YearClass, _YearMonthClass);
_YearMonthClass.prototype.getClassName = function() {
return this.__upper.getClassName()+"/"+"YearMonthClass";
};
_YearMonthClass.prototype.setMonth = function(month) { this.month = month; }
_YearMonthClass.prototype.getYearMonth = function() { return this.year+"/"+this.month; };
// ■「YearMonthDayClass」の定義
var _YearMonthDayClass = function() {
this.__upper.constructor();
document.writeln("YearMonthDayClassのコンストラクタが呼ばれたよ!<br>");
this.day = undefined;
};
var YearMonthDayClass = extend(YearMonthClass, _YearMonthDayClass);
_YearMonthDayClass.prototype.getClassName = function() {
return this.__upper.getClassName()+"/"+"YearMonthDayClass";
}
_YearMonthDayClass.prototype.setDay = function(day) { this.day = day; };
_YearMonthDayClass.prototype.getDate = function() { return this.year+"/"+this.month+"/"+this.day; };
// インスタンスを作成する
var obj = new YearMonthDayClass();
obj.setYear(2010);
obj.setMonth(10);
obj.setDay(1);
document.writeln("getDate : "+obj.getDate()+"<br>");
document.writeln("getYearMonth : "+obj.getYearMonth()+"<br>");
document.writeln("className : "+obj.getClassName()+"<br>");
// 保持しているフィールドの一覧を表示する
showObject(obj);
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>クラスの独自定義その6(継承の補足その2)</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
YearClassのコンストラクタが呼ばれたよ! YearMonthClassのコンストラクタが呼ばれたよ! YearMonthDayClassのコンストラクタが呼ばれたよ! getDate : 2010/10/1 getYearMonth : 2010/10 className : YearClass/YearMonthClass/YearMonthDayClass ■objの内容表示 obj : day obj : year obj : month obj : __upper obj : constructor obj : getClassName obj : setDay obj : getDate obj : setMonth obj : getYearMonth obj : setYear |
ものの本にこんなテクニックが載ってたので、とりあえず真似して書いてみました。使う使わないは別として、テクニックとしてこんなのがありますレベルの話です。
処理内容としては、上位クラスとして指定されたクラスのフィールドとメソッドを、下位クラスにコピーを行っています。関数については、下位クラスで定義済みのものはコピーしません。
・・・と、まぁ、それだけです。
| sample.js |
/*
* クラスの独自定義その7(クラスベースのオブジェクト指向っぽくする)
*/
function fieldCopy(derive, base, args) {
// apply関数はFunctionオブジェクトが持っている関数です。
// 処理が特殊で、処理ロジックは、baseのものが実行されますが、変更されるデータは引数で渡している
// derive(子クラスのインスタンス)となります。
// なので、この処理の結果、子クラスのフィールドに、nameとaddressが定義されます。
base.apply(derive, args);
var p = derive.constructor.prototype;
for (var prop in base.prototype) {
// ここでチェックをしているため、上位クラスと同名の下位クラスのメソッドが上書きされないようになっています
if (!p[prop]) {
p[prop] = base.prototype[prop];
}
}
}
function showObject(obj) {
document.writeln("■objの内容表示<br>");
for (var v in obj) {
document.writeln("obj : "+v+"<br>");
}
}
// ■BaseClassクラスの定義
var BaseClass = function() {
document.writeln("BaseClassのコンストラクタが呼ばれました!<br>");
this.name = undefined;
this.address = undefined;
}
BaseClass.prototype.getInfo = function() {
return "name : "+this.name+"/"+"address : "+this.address;
}
// ■UnderClassクラスの定義
var UnderClass = function() {
document.writeln("UnderClassのコンストラクタが呼ばれました!<br>");
fieldCopy(this, BaseClass, null);
this.tel = undefined;
}
UnderClass.prototype.getStatus = function() {
// 上位クラスのメソッドを呼ぶ
var result = BaseClass.prototype['getInfo'].apply(this);
return result+" / tel : "+this.tel;
}
var obj = new UnderClass();
obj.name = "ひなだお!";
obj.address = "夢の国"
obj.tel = "03-XXXX-XXXX";
document.writeln("getStatus : "+obj.getStatus()+"<br>");
showObject(obj);
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>クラスの独自定義その7(クラスベースのオブジェクト指向っぽくする)</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
UnderClassのコンストラクタが呼ばれました! BaseClassのコンストラクタが呼ばれました! getStatus : name : ひなだお!/address : 夢の国 / tel : 03-XXXX-XXXX ■objの内容表示 obj : name obj : address obj : tel obj : getStatus obj : getInfo |
JavsScriptには、C++やJavaのようにアクセス修飾子がありません。
クロージャにより疑似的に実現します(クロージャが入ってくる時点でもー、クラスのアクセス修飾子とか概念が別w)。
プライベートメンバとか特権メソッドなんて仰々しいタイトルですが、内容はたいした内容じゃないです^^;
ソースを見るとわかるかと。なので、以下に例を示します。
| sample.js |
/*
* プライベートメンバと特権メソッド
*/
var SampleClass = function() {
// コンストラクタ内で変数を定義する。結果的にこれらがプライベートメンバ変数となる。
var _name;
var _address;
// アクセッサーを定義。これらの中で_name/_addressを使用しているため、_name/_addressはクロージャとなる
// 以下のメソッドを特権メソッドという。これらは普通に外部より呼び出すことが可能。
this.getName = function() { return _name; }
this.setName = function(name) { _name = name; }
this.getAddress = function() { return _address; }
this.setAddress = function(address) { _address = address; }
}
SampleClass.prototype.getInfo = function() {
return this.getName()+"/"+this.getAddress();
}
var obj = new SampleClass();
obj.setName("ひなだお!");
obj.setAddress("おいたんだえ!");
document.writeln("result1 : "+obj.getInfo()+"<br>");
var obj = new SampleClass();
obj.setName("兄貴");
obj.setAddress("新日暮里");
document.writeln("result2 : "+obj.getInfo()+"<br>");
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>プライベートメンバと特権メソッド</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
result1 : ひなだお!/おいたんだえ! result2 : 兄貴/新日暮里 |
JavaScriptには、名前空間の言語仕様によるサポートはありません。
なので、空のクラスを作成し、そこに関数などをセットしていくことで、疑似的に名前空間を実現します。
以下に例を示します。
| sample.js |
/*
* 名前空間について
*/
var Hina = function() {};
Hina.SampleClass = function() {
this.name = undefined;
}
Hina.SampleClass.prototype.getInfo = function() {
return "にゃん";
}
var obj = new Hina.SampleClass();
document.writeln("result : "+obj.getInfo());
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>名前空間について</title>
</head>
<body>
<script type="text/javascript" src="sample.js"></script>
<noscript>JavaScriptが使用できない環境です!</noscript>
</body>
</html>
|
| 実行結果 |
result : にゃん |
ブラウザオブジェクトとは、以下のような階層構造をもつ、オブジェクトの塊です。
ブラウザを起動し、JavaScriptを使用する時にはこれらのオブジェクトのインスタンスが生成され、グローバル変数として使えるようになっています。
表の中の()の中の値が実際のオブジェクトの変数名となります。
左のWindowオブジェクトがトップにあり、そこから階層構造となっています。二階層目のWindowオブジェクトは自分でウインドウを作った場合に使用します(名前を任意につけることができます)。
| Windowオブジェクト (window) |
→ | Windowオブジェクト (任意) |
||||||||
| Screenオブジェクト (screen) |
||||||||||
| Documentオブジェクト (document) |
→ | Formオブジェクト配列 (forms) |
→ | 画面コンポーネント (elements) |
→ | Button | ||||
| CheckBox | ||||||||||
| FileUpload | ||||||||||
| Password | ||||||||||
| Radio | ||||||||||
| Reset | ||||||||||
| Select | → | チェックボックスやリストボックスの選択肢 (options) |
||||||||
| Submit | ||||||||||
| Text | ||||||||||
| Textarea | ||||||||||
| Ahchorオブジェクト配列 (anchors) |
||||||||||
| Imageオブジェクト配列 (images) |
||||||||||
| Locationオブジェクト (location) |
||||||||||
| Navigatorオブジェクト (navigator) |
||||||||||
| Historyオブジェクト (history) |
上記のような構成になっていることを念頭に置いて、JavaScriptを書いていきます。
通常、一番左のオブジェクトは存在を意識しません。具体的には、window.document.forms・・・と記述するのではなく、document.forms・・・・と、一番上位のwindowを記述しません。これについては、サンプルを見てもらえればわかると思います。
なお、このページでは、Locationオブジェクト、Navigatorオブジェクト、Historyオブジェクトについては扱いません。使用頻度が低そうだし・・・。
確認ダイアログのような、モーダルダイアログを表示する関数と、モードレスダイアログを表示する関数についてです。
メッセージを表示するレベルの簡単なモーダルダイアログを出すにはJavaScriptでは、alert/confirm/promptと3つ関数があります(windowクラスの関数です)。3つともモーダルですが、若干出来ることに違いがあります。
指定したURLの画面を開いたモーダルを開くにはshowModalDialogを使用します。
モードレスダイアログを開くにはopen関数にて表示するURLを指定して実行します。
open関数はIEのデフォルトでは動かないようになってるみたいです(Firefoxなら動いた)
上記のリンク先で使用しているソースを以下に示します。
| sample.js |
/*
* ダイアログボックスとサブウインドウについて
*/
function dialogAlert() {
alert("ひなだお!");
return false;
}
function dialogConfirm() {
var result = confirm("ひぎぃ");
document.getElementById("dlg1").innerHTML ="dialog2 result : "+result;
return false;
}
function dialogPrompt() {
var result = prompt("わろたw","うはwww");
document.getElementById("dlg2").innerHTML ="dialog3 result : "+result;
return false;
}
function dialogShowModalDialog() {
var url = "modalSample.html";
var option = "width=800, height=600, scrollbars=yes, location=no";
document.getElementById("dlg3").innerHTML = "モーダルを開きますよ!";
var res = showModalDialog(url, window, option);
alert(res);
}
// 開いたウインドウのwindowオブジェクトを保持するためのグローバル変数
var windowHandle;
function dialogOpen() {
var url = "http://alctail.sakura.ne.jp/";
var windowName = "subwindowDESUYO!"
var option = "width=800, height=600, scrollbars=yes, location=no";
if (!windowHandle) {
windowHandle = open(url, windowName, option);
}
}
// この関数の処理で、openしたウインドウを親ウインドウからclose可能です
function dialogSubWindowClose() {
if (windowHandle) {
if (!windowHandle.closed) {
// ウインドウが生成されており、かつ、閉じられていない場合は閉じる
windowHandle.close();
windowHandle = undefined;
}
}
}
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" language="JavaScript" src="sample.js"></script>
<title>ダイアログボックスとサブウインドウについて</title>
</head>
<body>
<input type="button" value="alert関数" onclick="dialogAlert()">
<input type="button" value="confirm関数" onclick="dialogConfirm()">
<input type="button" value="prompt関数" onclick="dialogPrompt()">
<input type="button" value="showModalDialog関数" onclick="dialogShowModalDialog()">
<input type="button" value="open関数" onclick="dialogOpen()">
<input type="button" value="close関数" onclick="dialogSubWindowClose()">
<hr>
<div id="dlg1"></div>
<div id="dlg2"></div>
<div id="dlg3"></div>
</body>
</html>
|
JavaScript中から、formタグの中にあるコンポーネント(inputタグとかのことを指す)にアクセスする方法についてです。
サンプルで、名前を指定してコンポーネントにアクセスしていますが、この「名前」とは、各タグのname属性で指定された名前のことです。
以下にサンプルを示します。
| sample.js |
/*
* formオブジェクトについて
*/
function init() {
// フォーム内のコンポーネントにアクセスする
document.form1.input1.value = "にゃん";
document.form2.input1.value = "うひw";// 以下の方法でもコンポーネントにアクセス可能
// 以下3種類の書き方も可能ですが、上記の書き方が一番わかりやすいと思います・・・。
// コンポーネントを配列風にアクセスする(配列に入ってくる順序はブラウザ依存かも)
//document.forms[0].elements[0].value="にゃん";
//document.forms[1].elements[0].value="うひw";
// コンポーネントを連想配列風にアクセスする
//document.forms['form1'].elements['input1'].value = "にゃん";
//document.forms['form2'].elements['input1'].value = "うひw";
// コンポーネントを連想配列風にアクセスするの省略形
//document['form1']['input1'].value = "にゃん";
//document['form2']['input1'].value = "うひw";
}
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" language="JavaScript" src="sample.js"></script>
<title>formオブジェクトについて</title>
</head>
<body onload="init()">
<form name="form1">
入力1:<input name="input1" type="text" size="50"/>
</form>
<form name="form2" id="sample2">
入力2:<input name="input1" type="text" size="50"/>
</form>
</body>
</html>
|
selectがmultipleだった場合など・・・のJavaScriptによるコンポーネントアクセスのサンプルです。
ラジオボタンやチェックボックスはoptionsのcheckedフィールドの値で、選択されている/されていないが判定されます。
コンボボックスやリストボックスはoptionsのselectedフィールドの値で、選択されている/されていないが判定されます。
| sample.js |
/*
* formオブジェクトについて その2(いくつかのコンポーネントのサンプル)
*/
// bodyタグのonloadでthisを渡すとwindowオブジェクトが取得される
function init(windowObj) {
windowObj.document.getElementById('msg').innerHTML="初期化したよ!";
}
// チェックボックスとリストボックスを全選択する
function buttonClick1(form) {
// チェックボックスにチェックをいれる
for (var i=0; i<form.check_box1.length; i++) {
form.check_box1[i].checked=true;
}
// リストボックスを全選択する
for (var i=0; i<form.listbox1.options.length; i++) {
form.listbox1.options[i].selected=true;
}
}
// 現在選択されている項目を調べる
function buttonClick2(form) {
var msg;
// チェックボックスを調べる
for (var i=0; i<form.check_box1.length; i++) {
if(form.check_box1[i].checked) {
msg += "checkbox : " + form.check_box1[i].value + "\n";
}
}
// リストボックスを全選択する
for (var i=0; i<form.listbox1.options.length; i++) {
if (form.listbox1.options[i].selected) {
msg += "Listbox : " + form.listbox1.options[i].value + "\n";
}
}
// ダイアログで表示する
alert("選択されているもの一覧:\n"+msg);
}
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" language="JavaScript" src="sample.js"></script>
<title>formオブジェクトについて その2(いくつかのコンポーネントのサンプル)</title>
</head>
<body onload="init(this)">
<form name="form1">
<fieldset>
<legend>チェックボックス</legend>
<input type="checkbox" name="check_box1" value="チェックボックス1" checked>デュフフコポォ<br>
<input type="checkbox" name="check_box1" value="チェックボックス2" checked>オウフドプフォ<br>
<input type="checkbox" name="check_box1" value="チェックボックス3" checked>フォカヌポウ<br>
</fieldset>
<fieldset>
<legend>リストボックス</legend>
<select name="listbox1" size="4" multiple>
<option value="test1_selected">だらしねぇという 戒めの心
<option value="test2_selected">歪みねぇという 賛美の心
<option value="test3_selected">仕方ないという 許容の心
<option value="test4_selected">それが大切だね。
</select>
</fieldset>
<input type="button" value="全選択" onclick="buttonClick1(this.form)">
<input type="button" value="現在の選択状況を調べる" onclick="buttonClick2(this.form)">
</form>
<div id="msg"></div>
</body>
</html>
|
モニタの解像度などのの情報が取得できます。
そんだけかとw
以下サンプルを示します。
| sample.js |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" language="JavaScript" src="sample.js"></script>
<title>Screenオブジェクトについて</title>
</head>
<body onload="init(this)">
<div id="msg"></div>
</body>
</html>
|
| 上記JavaScriptを呼び出す画面のHTML |
/*
* Screenオブジェクトについて
*/
// bodyタグのonloadでthisを渡すとwindowオブジェクトが取得される
function init(windowObj) {
var msg = "横(pixel):" + screen.width + "縦(pixel):" + screen.height;
windowObj.document.getElementById('msg').innerHTML=msg;
}
|
今まで、サンプルコードの中でも使ったりしていましたが、document.getElementByIdなどの関数で、タグのid属性値でマッチングを行い、そのコンポーネントを取得する・・・ということが可能です。
DOMの実装がJavaScriptにも存在し、それを使用することができます(解析されたデータに対し、JavaScriptにDOMインターフェイスが用意されている)。
getElementById以外にも、class属性をキーにノード取得を行うgetElementByClassName関数や、タグ名をキーにノード取得をするgetElementByTagName関数などがあります。
やりたいことにより各種使い分けが行われると思います(・・・が通常、タグにid属性を必ず指定するように取決めをして、getElementById関数を使うケースが多いかもしれません。ちなみに、id値はかぶってはダメ。)。
以下に、getElementByIdを使ったノードの取得と、そのノードから属性値の取得するサンプルを示します。
| sample.js |
/*
* DOMによる各ノードへのアクセス
*/
function init(windowObj) {
// HTMLの中から特定のノードを取得する
var inputTextNode = windowObj.document.getElementById("inputText");
// 属性値の一覧を取得する
var attrMsg = "";
var attrArray = inputTextNode.attributes; // この戻り値がNamedNodeMap
for (var i=0; i<attrArray.length; i++) {
var attr = attrArray.item(i);
if (attr.nodeValue) {
// 値を取得する際以下どちらでの記述方法でもOK
attrMsg += "属性名 : "+attr.nodeName + "\n";
attrMsg += "値 : " + attr.nodeValue+"\n\n";
// attrMsg += "属性名 : "+attr.nodeName + "\n";
// attrMsg += "値 : " + inputTextNode.getAttribute(attr.nodeName) + "\n\n";
}
}
// 結果をダイアログボックスで表示する
alert(attrMsg);
}
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" language="JavaScript" src="sample.js"></script>
<title>DOMによる各ノードへのアクセス</title>
</head>
<body onload="init(this)">
<form id="form1">
<textarea id="inputText" rows="8" cols="40"></textarea>
</form>
</body>
</html>
|
ノード追加・・・、つまりJavaScript側から、HTMLの内容を追加できます。無論、DOM経由で追加、変更、削除が可能ですが、ここでは、追加・変更のサンプルのみ扱います。
ノードの配下にテキストを挿入する場合、createTextNodeを使っていますが、用途によっては別関数を使わなければならないみたい。それについては、扱いません。
タグの中身を書き換えたい場合、innnerHTML(こちらでやっているような処理)を指定して、そちらに内容を出力してもよいです。
DOMを使って行うことも可能なので、今回はこちらのサンプルを以下に示します。
| sample.js |
/*
* DOMによるノードの追加/変更
*/
function init(windowObj) {
// ノードの変更をする(テキストボックスの値を変更する)
var inputTextNode = windowObj.document.getElementById('inputTarget');
inputTextNode.setAttribute('value', 'ゆがみねぇな');
// divタグ以下に、ノードを追加する
// 追加対象のノードを取得する
var divTag = windowObj.document.getElementById('target');
// 追加するノードを作成する
var pTag = windowObj.document.createElement('p');
pTag.setAttribute('align', 'center'); // pタグの属性値を設定
// Pタグの要素として挿入する文字列を作成する
var text = windowObj.document.createTextNode("妖精哲学の三信");
pTag.appendChild(text);
// ノードを追加する
divTag.appendChild(pTag);
}
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" language="JavaScript" src="sample.js"></script>
<title>DOMによるノードの追加/変更</title>
</head>
<body onload="init(this)">
<form id="form1">
<input type="text" id="inputTarget" size=50 value="">
<div id="target"></div>
</form>
</body>
</html>
|
イベントリスナという概念があります。
HTMLのタグにonclick="〜〜"と、JavaScriptの関数を指定したりしますが、こちらは、OnClickなどのイベントに対し、JavaScript上で設定が可能です。このイベントの設定、そして削除はjavaScript上で任意に行うことが可能です。
ただ、こちらは、ブラウザ間で操作が統一されていません(IEとその他のブラウザで仕様が異なる)。これには対応方法があるので、大丈夫です。
以下に例を示します。
| sample.js |
/*
* イベントリスナの使い方 その1
*/
function init() {
// イベント設定対象の画面コンポーネントを取得する
var buttonElement = document.getElementById('btn1');
// イベントリスナーを登録する
addEventListener(buttonElement, 'click', onClickHandler)
}
// イベントリスナを登録する関数
function addEventListener(element, eventName, eventHandler) {
if (element.addEventListener) {
// IE以外向け
element.addEventListener(eventName, eventHandler, false);
} else if (element.attachevemt) {
// IE向け
element.attachEvent('on'+eventName, eventHandler);
} else {
throw new Error('イベントリスナー使用不可');
}
}
// イベントリスナを削除する関数
function removEventListener(element, eventName, eventHandler) {
if (element.removeEventListener) {
// IE以外向け
element.removeEventListener(element, eventHandler);
} else if (element.detachEvent) {
element.detachEvent('on'+eventName, eventhandler);
} else {
throw new Error('イベントリスナー使用不可');
}
}
// イベントの発生元を取得する
function getSourceElement(event) {
if (event.target) {
// IE以外向け
return event.target;
} else if (window.event) {
// IE向け
return window.event.srcElement;
}
}
var gCounter=0;
function onClickHandler(event) {
if (gCounter++ < 5) {
var msg = [];
var divTag = document.getElementById('msg');
msg.push(divTag.innerHTML);
msg.push("イベント発生元ID : "+getSourceElement(event).id);
msg.push("イベントタイプ : "+event.type);
divTag.innerHTML = msg.join('<br>');
} else {
// カウントが5以上なら、イベントリスナーを削除する。
var btnElement = document.getElementById('btn1')
removEventListener(btnElement, 'click', onClickHandler);
}
}
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" language="JavaScript" src="sample.js"></script>
<title>イベントリスナの使い方 その1</title>
</head>
<body onload="init()">
<input type="button" value="ボタン" id="btn1">
<div id="msg"></div>
</body>
</html>
|
上位にあるタグと、下位にあるタグにイベントリスナを設定した場合、下位にあるタグでのイベントが発生した場合、上位のタグにもそのイベントが発生したことが伝えられ、設定したハンドラ関数が呼び出されます。これをバブリングと呼びます。
それでは不都合なケースがあるため、それをキャンセルする処理について、例を示します。
三信の中で、「だらしねぇという 戒めの心」のみ、ダイアログが一回しかでません。これは、バブリングがキャンセルされているからです。
ほかの2つは、クリックするたびに、二回ダイアログが表示されます。
| sample.js |
/*
* イベントリスナの使い方 その2
*/
function init() {
// イベント設定対象の画面コンポーネントを取得する
var divTag = document.getElementById('msg');
var divTag1 = document.getElementById('msg1');
var divTag2 = document.getElementById('msg2');
var divTag3 = document.getElementById('msg3');
// イベントリスナーを登録する
addEventListener(divTag, 'click', onClickHandler);
addEventListener(divTag1, 'click', onClickHandler1);
addEventListener(divTag2, 'click', onClickHandler2);
addEventListener(divTag3, 'click', onClickHandler3);
}
// イベントリスナを登録する関数
function addEventListener(element, eventName, eventHandler) {
if (element.addEventListener) {
// IE以外向け
element.addEventListener(eventName, eventHandler, false);
} else if (element.attachevemt) {
// IE向け
element.attachEvent('on'+eventName, eventHandler);
} else {
throw new Error('イベントリスナー使用不可');
}
}
// バブリングを行わないようにする関数
function stopBubbling(event){
if (event.stopPropagation) {
// IE以外向け
event.stopPropagation();
} else if (window.event) {
window.event.cancelBubble = true;
}
}
function onClickHandler(event) {
alert('妖精哲学の三信');
}
function onClickHandler1(event) {
alert('だらしねぇという 戒めの心');
stopBubbling(event);
}
function onClickHandler2(event) {
alert('歪みねぇという 賛美の心');
}
function onClickHandler3(event) {
alert('仕方ないという 許容の心');
}
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" language="JavaScript" src="sample.js"></script>
<title>イベントリスナの使い方 その2</title>
</head>
<body onload="init()">
<div id="msg">
妖精哲学の三信
<div id ="msg1">だらしねぇという 戒めの心</div>
<div id ="msg2">歪みねぇという 賛美の心</div>
<div id ="msg3">仕方ないという 許容の心</div>
それが大切だね。
</div>
</body>
</html>
|
submitする際、return falseをすれば、送信をキャンセルできますが、イベントリスナを使った場合、以下のようにします。
| sample.js |
/*
* イベントリスナの使い方 その3
*/
function init() {
var formTag = document.getElementById('sampleForm');
addEventListener(formTag, 'submit', onSubmitHandler);
}
// イベントリスナを登録する関数
function addEventListener(element, eventName, eventHandler) {
if (element.addEventListener) {
// IE以外向け
element.addEventListener(eventName, eventHandler, false);
} else if (element.attachevemt) {
// IE向け
element.attachEvent('on'+eventName, eventHandler);
} else {
throw new Error('イベントリスナー使用不可');
}
}
// 処理をキャンセルする関数
function cancelEvent(event) {
if (event.preventDefault) {
// IE以外のブラウザ向け
event.preventDefault();
} else if (window.event) {
// IE向け
window.event.returnValue = false;
} else {
throw new Error('使用不可');
}
}
function onSubmitHandler(event) {
var result = confirm('送信します?');
if (!result) {
// 送信をキャンセルする
cancelEvent(event);
}
}
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" language="JavaScript" src="sample.js"></script>
<title>イベントリスナの使い方 その3</title>
</head>
<body onload="init()">
<form id="sampleForm">
<input type="submit" value="送信"/>
</form>
</body>
</html>
|
Ajaxの説明は省略します。
非同期通信を行うのに、XMLHttpRequestオブジェクトが必要ですが、IEについてブラウザのバージョンにより作成するオブジェクトが異なる・・・という話があるかと思いますが、それはIE7より前の話のようです。なので、この話はここでは無視をします。
常に、XMLHttpRequest関数で作成をすることを前提とします。なので、IEを使う場合、IE7以上のバージョンでなければ、動かないです。ほかのブラウザでは常に、XMLHttpRequest関数を使ってれば問題ないみたい。
Ajaxでは仕様として、他ドメインに対する通信はできません。なので、別箇外部との通信を肩代わりしてくれるプロキシを内部に作成する必要があります。
今回のサンプルで示した、ajaxHandler関数(イベントのコールバック関数として設定した関数)の中でイベントを判別しそれぞれ、ハンドリングを行います。
readyStateの値は以下の仕様みたいです。
| 戻り値 | 説明 |
| 0 | 処理用んほオブジェクトを初期化中の状態 |
| 1 | 要求を投げるサーバーとの接続が確立した状態 |
| 2 | サーバーから応答が戻ってきている途中の状態 |
| 3 | サーバーからの応答データの本体を受信中の状態 |
| 4 | サーバーが応答データを受信完了の状態 |
てきとーに訳したが、とりあえずここを見るべき。
あと、statusでHTTPのステータスをチェックしています。おなじみのHTTPのリターンコードです。200は正常なので、そのときのみ処理しています。500系などはサーバー内部エラーですね。
今回のサンプルですが、サーバーへ要求を投げる必要があるので、こんなサーブレットを作成してみました。そしてこれをtomcat7で動かしています。
このtomcatのプロジェクト内に、HTMLファイルと、sample.jsファイルを置いて(WebContent直下)動かしています。
| AjaxSample1Servlet.java |
package servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name="AjaxSample1Servlet",urlPatterns={"/AjaxSample1Servlet"})
public class AjaxSample1Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("呼び出されました!");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html; charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.println("<html><body>");
writer.println("<p>妖精哲学の三信</p>");
writer.println("<p>だらしねぇという 戒めの心</p>");
writer.println("<p>歪みねぇという 賛美の心</p>");
writer.println("<p>仕方ないという 許容の心</p>");
// 送信された値を出力する
Enumeration<String> e = req.getParameterNames();
while(e.hasMoreElements()) {
String key = e.nextElement();
String value = req.getParameter(key);
System.out.println(key+" : "+value); // とりあえず送られてきた値は標準出力に出すだけ
}
writer.println("</body></html>");
}
}
|
以下が今回のサンプルです。
今回はHTTPのGETメソッドを使う場合の例です。POSTの場合のケースは次で説明します。
| sample.js |
/*
* Ajaxの基本的な例
*/
// 初期化用関数
function init() {
// submit時のイベントリスナを登録
var formTag = document.getElementById('sampleForm');
addEventListener(formTag, 'submit', onSubmitHandler);
}
// イベントリスナを登録する関数
function addEventListener(element, eventName, eventHandler) {
if (element.addEventListener) {
// IE以外向け
element.addEventListener(eventName, eventHandler, false);
} else if (element.attachevemt) {
// IE向け
element.attachEvent('on'+eventName, eventHandler);
} else {
throw new Error('イベントリスナー使用不可');
}
}
// 処理をキャンセルする関数
function cancelEvent(event) {
if (event.preventDefault) {
// IE以外のブラウザ向け
event.preventDefault();
} else if (window.event) {
// IE向け
window.event.returnValue = false;
} else {
throw new Error('使用不可');
}
}
// 送信ボタン押下時の処理
function onSubmitHandler(event) {
var targetUrl = 'http://localhost:8080/AjaxSample1/AjaxSample1Servlet';
// 非同期通信のインスタンス生成
var request = XMLHttpRequest();
request.onreadystatechange = function() {
ajaxHandler(request);
};
// GETメソッドの場合。引数をつけたい場合は、targetUrlに?をつけて引数を書いていけばOK
// 例:targetUrl+'?key=value' (普通のGETメソッドの時と書式は同じ)
// 値に対し、encodeURIComponentでURLエンコードしたものを送信するが、今回は省略
targetUrl += '?key1='+"aaaaa";
targetUrl += '&key2='+"bbbbb";
targetUrl += '&key3='+"ccccc";
request.open('GET', targetUrl, true);
request.send(null);
// イベントリスナで、実際にフォームのsubmitが行われないように制御
cancelEvent(event);
}
// 非同期通信のイベントハンドラ
function ajaxHandler(request) {
if (request.readyState == 4) {
if (request.status == 200) {
// 以下のプロパティより、応答のXMLを取得することが可能(このデータを解析する)
// var requestXml = request.responseXML;
// 以下のプロパティより、応答データをテキストで取得することも可能
var requestText = request.responseText;
showResult(requestText);
} else {
showResult("HTTP error : " + request.status);
}
} else {
showResult("処理中です... : " + request.readyState);
}
}
function showResult(msg) {
var divTag = document.getElementById('msg');
if (divTag.innerHTML) {
divTag.innerHTML = divTag.innerHTML + msg+"<br>";
} else {
divTag.innerHTML = msg+"<br>";
}
}
|
| 上記JavaScriptを呼び出す画面のHTML |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" language="JavaScript" src="sample.js"></script>
<title>Ajaxの基本的な例</title>
</head>
<body onload="init()">
<form id="sampleForm">
<input type="submit" value="送信"/>
<div id="msg"></div>
</form>
</body>
</html>
|
| 実行結果 |
処理中です... : 1 処理中です... : 1 処理中です... : 2 処理中です... : 3 妖精哲学の三信 だらしねぇという 戒めの心 歪みねぇという 賛美の心 仕方ないという 許容の心 |
前回の続きです。
前回はGETメソッドでサーバーにデータを送りましたが、POSTをする場合の例を以下に示します(ソースは前回のを少しいじっただけです)。
修正箇所は微々たるものです。
修正したonSubmitHandler関数の内容を以下に示します。
| sample.jsのonSubmitHandler関数 |
// 送信ボタン押下時の処理
function onSubmitHandler(event) {
var targetUrl = 'http://localhost:8080/AjaxSample1/AjaxSample1Servlet';
// 非同期通信のインスタンス生成
var request = XMLHttpRequest();
request.onreadystatechange = function() {
ajaxHandler(request);
};
// ■GETの場合
// GETメソッドの場合。引数をつけたい場合は、targetUrlに?をつけて引数を書いていけばOK
// 例:targetUrl+'?key=value' (普通のGETメソッドの時と書式は同じ)
// 値に対し、encodeURIComponentでURLエンコードしたものを送信するが、今回は省略
// targetUrl += '?key1='+"aaaaa";
// targetUrl += '&key2='+"bbbbb";
// targetUrl += '&key3='+"ccccc";
// request.open('GET', targetUrl, true);
// request.send(null);
// POSTの場合
request.open('POST', targetUrl, true);
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
// POSTするデータを作成する
var sendData ="key1=aaaaa&key2=bbbbb&key3=ccccc";
request.send(sendData);
// イベントリスナで、実際にフォームのsubmitが行われないように制御
cancelEvent(event);
}
|