2013年7月24日水曜日

連載:ちょっとディープなXPages 第2-4回~JavaScriptをもっと知ろう

みなさん!XPages開発していますか!?
さて今回は型変換の続きで、文字や日付型から数値への型変換を書いていきます。

数値型への型変換

文字列→数値

これについては、前々回でお話していますが、おさらいしておきましょう
  • JavaScriptのビルドイン関数にparseInt()がありますが、これは使わない
  • 代わりに Number() を使用する
  • 自動型変換を利用した方法でも良い (+"08")
ということでした。理由については、前々回の記事を参照してください。
ここで1つ注意。new Number() としてはダメです。下記のように不思議な現象が起きます。
var str = '08',
    num_a = Number(str),
    num_b = +str,
    num_c = new Number(str);
print(num_a); // 8
print(num_b); // 8
print(num_c); // 8 (chromeのconsoleで出せば Number{} と表示されます)
print(num_a === num_b); // true
print(num_a === num_c); // false ←?
じつは、new をつけると数値型ではなく数値オブジェクト型になるためです。数値はオブジェクトではなくプリミティブですが、new をつけると数値をラップしたオブジェクトになるため、数値とは異なるものになるからです。
ちょっと話が難しくなりましたね。今はわかんねーよという方は、とりあえずnew Number() を使わないようにしましょう。(勉強してわかるようになろうね!)
あ、下記も同じことが言えるので、使用禁止です。(前々回でやっとけばよかったねー)
// ダメな例
new String();
new Number();
new Boolean();
あれ? (1).toString() とかできるよね?プリミティブ値にはメンバー無いんじゃない? と思った方がいるかもしれません。実は、内部で暗黙的にオブジェクトに変換しているんですね。
(1).toString();
↓内部的には
(new Number(1)).toString();
として処理されます。便利ではあるんですが、ややこしさの一因にもなってますね(^^;

日付→数値

これは、Date.getTime()で変換する他ありません。前回「日付→文字列」でも書いた方法ですね。
こ れで得られる値は、タイムゾーンに関係なくUTCの1970/1/1 0時からの経過ミリ秒数で得られるので、日付型で保持できない(例えばプレーンなテキスト、JSONなど)場合にこの数値に変換して、使用するときには Data.setTime()で日付型に戻すと良いでしょう。

Boolean値の変換

Booleanはまとめたほうがわかりやすいかなと思い最後まとめました。

対数値

Booleanは、true -> 1、false -> 0 の関係にあり、それぞれ相互に変換されます。また、数値は0以外("以下"ではない!!)はtrue、0はfalseに変換されます。
print(Number(true)); // 1
print(Number(false)); // 0
print(Boolean(0)); // false
print(Boolean(1)); // true
print(Boolean(100)); // true
print(Boolean(-1)); // true
繰り返しになりますが、new Boolean() は使わないように。

対文字列

文字列は、空文字(長さが0の文字列)以外は、trueになります。
またBoolean値を文字列に変換すると、"true","false"になります。
・・・気づきました?
そう、false→文字列で"false"ですが、"false"→Booleanで true になってしまいます!
print(Boolean("a")); // true
print(Boolean("")); // false
var t = true,
    f = false,
    f_a;
print(String(t)); // "true"
f_a = String(f);
print(f_a); // "false"
print(Boolean(f_a)); // true

null と undefined

これらはBoolean型に変換すると、falseになります。
変数に値が割り当てられたかもしくはnullの場合は、処理しない・・・などどいうシーンでよく使用しますね。
var f = function(a, b){
    // !!は、自動型変換を利用した変換方法
    // ifの場合は()内を自動的に変換するため不要だが
    // 明示的に示すことで、意図を明確にできる。
    if(!!(a) && !!(b)){
        return 'o';
    }
    return 'x';
}
print(f("i")); // 'x' bがundefined
print(f(null, "i")); // 'x' aがnull
print(f("i", null)); // 'x' bがnull
print(f(1, "i")); // 'o'

型変換はおしまい

次は、JavaScriptのいろいろな実装パターンについて書いていこうかと思います。
海老原 賢次(EBIHARA Kenji)
リコーITソリューションズ株式会社(RICOH IT SOLUTIONS CO.,LTD.)
鹿児島ソリューション部(Kagoshima Department)

連載:ちょっとディープなXPages 第2-3回~JavaScriptをもっと知ろう

みなさん!XPages開発していますか!?
さて今回は、JavaScriptの型変換について記載してみたいと思います。SSJSについてもやるので、JavaScriptが続いてなんだかXPagesから離れてるな~なんて思っている方、ぜひ見てくださいね。
型変換についての説明は、文字列、数値、日付型の相互変換について書いていきます。

文字列型への型変換

数値→文字列

Number型にtoString()メソッドが存在するので、これを使うことで良いでしょう。また、String関数や自動型変換を利用することもできます。
また、自動型変換を利用した記述の少ない方法もよく取られます。
/* SSJS, CSJS */
var num0 = 123,
    num1 = 456.789,
    num2 = 987.654;
//SSJSの場合はprintを使う
console.log('num0:' + num0.toString()); // "num0:123"
console.log('num1:' + String(num1)); // "num1:456.789"
console.log('num2:' + (num2+'')); // "num2:987.654"
// 一番下は、'num2:' + num2 でも同じ結果だが、
//num2を数値から文字列に型変換することを名確認するため、(num2+'')とする。
書式を指定した変換の仕組みは、ビルドイン関数にありませんが、XPagesでは書式変換用の関数を利用できます。
CSJSではDojoの関数"dojo.number.format"を利用します。
dojo.numberオブジェクトを使用するためには、XPagesのリソース→追加→Dojoモジュール→Dojoモジュール名"dojo.number"を指定します。
イメージ
/* CSJS */
var num0 = 123456;
console.log('num0-1:' + dojo.number.format(num0,{pattern: '0000000000'})); // "num0-1:0000123456"
console.log('num0-2:' + dojo.number.format(num0,{pattern: '#,###.00'})); // "num0-2:123,456.00"
※ 書式については、こちらにを参照してください。
SSJSについては、I18n.toString()を使用することで、任意のフォーマットに安全に変換出来ます。
/* SSJS */
var num0 = 123456;
print('num0-1:' + I18n.toString(num0, '0000000000')); // "num0-1:0000123456"
print('num0-2:' + I18n.toString(num0, '#,###.00')); // "num0-2:123,456.00"

日付→文字列

JavaScriptのDateオブジェクトには、toString()メソッドが存在しますが、これはローケーションによって書式が異なり、戻り値の予測が困難なため、使用してはいけません。
XPagesでは数値型と同じようにCSJS、SSJSに関数が用意されていますので、それを利用しましょう。
dojo.date.localeオブジェクトを使用するので、XPagesのリソース→追加→Dojoモジュール→Dojoモジュール名"dojo.date.locale"を指定してください。
イメージ
/* CSJS */
var date = new Date();
//日付のみ変換
console.log('date0-1:' +
  dojo.date.locale.format(date,{datePattern: 'yyyy/MM/dd', selector:'date'})); // "date0-1:2013/07/01"
//日時
console.log('date0-2:' +
  dojo.date.locale.format(date,{datePattern: 'yyyy/MM/dd HH:mm:ss'})); // "date0-2:2013/07/01 17:25:31"
/* SSJS */
var date = new Date();
//日付のみ変換
print('date0-1:' + I18n.toString(date, "yyyy/MM/dd")); // "date0-1:2013/07/01"
//日時
print('date0-2:' + I18n.toString(date,"yyyy/MM/dd HH:mm:ss")); // "date0-2:2013/07/01 17:25:31"
日付から文字列への変換で、注意しなければならないのは日付、時刻をユーザーのタイムゾーンに合わせる必要がある、ということです。
日本国内のみのアクセスに限定されるのであれば意識することはないですが、複数のタイムゾーンからのアクセスが考えられる場合は必ず配慮してください。
サーバーとクライアントとのタイムゾーンが違う場合、サーバーでDateオブジェクトから文字列に変換すると、通常はサーバーのタイムゾーンで変換され、レンダリングされるので、ユーザーには間違った日時が出力されてしまいます。
XPagesでは、SSJSでの日付の処理をクライアントのタイムゾーンに合わせることができます。DBのプロパティで設定出来ます。
イメージ
こ の設定を行うとXPagesは、初回アクセス時にブラウザのタイムゾーンを取得するためのレスポンスを出力し、クライアント側でJavaScriptに よって自動的にタイムゾーンの取得と、サーバーへの送信が行われ、サーバーがセッション情報として保持する、という動作をするようです。
また、XPagesをJSONを使用したWebサービスとして使うような場合では、この支援が受けられない場合があります。
そういった場合には、JavaScriptビルドインのDate.getTime() / Date.setTime() を使用することで、数値を介して変換します。
getTime() / setTime() はタイムゾーンに関係なく、UTCの1970/1/1からその日付までのミリ秒値(Tick値)を出力/入力できますので、タイムゾーンを意識することなく安全で確実に日時情報のデータの送受信ができます。
//サーバー側
var date = new Date(),
    retObj = {
        tick: date.getTime();
    }
//レスポンスDobyに書き出し(例
writer.write(retObj.toJSON());

//-クライアント側-------------
//--ajax通信については後日やります
var resDate = new Date();
$.ajax({
  url: "test.xsp",
  context: document.body,
  success: function(resBody){
   var retObj = xsp.fromJson(resBody);
   resDate.setTime(retObj.tick); 
  }
});

まだまだ続くよ

今日はこのへんで。次回は数値型、日付型への変換について書く予定です。
JavaScriptが続きますが、もう少しお付き合いを

2013年7月1日月曜日

連載:ちょっとディープなXPages 第2-2回~JavaScriptをもっと知ろう

みなさん!XPages開発していますか!? さて、今回は前回からの引き続きで、JavaScriptについて書いていきます。

JavaScript禁忌技(続き)

やるなよ、絶対にやるなよ。

parseInt()での数値変換は禁止

parseInt()は、文字列を数値に変換する便利な関数ですが、この関数の使用を十分理解していない場合、バグを生む原因にもなります。
IE8以降か、IE9以降のIE8モードで下記を実行してみてください。
alert(parseInt('07'));
alert(parseInt('08'));
"07"は"7"と表示されましたが、"08"は"0"が表示されました。おかしいですね?なんででしょう。
実は、parseIntには第2引数を渡すことができて、これは変換する進数の値です。
渡した文字列を16進数として扱いたい場合は、"16"を渡します。
alert(parseInt('0xFF'));
//→256 が表示される。
第2引数が渡されない場合は、文字列の内容によって進数が自動的に決定されるのですが、"0"で始まるものは8進数として処理しようとします。
よって、"08"は8進数として変換しようとしてしまいます。
しかし、"08"は8進数としては不正な文字列のため、(エラーになればいいのですが)0を返してしまいます。
最近のブラウザでは、"0x"で始まれば16進数、そうでない場合は10進数として処理するように仕様が変更されているので、この問題はおきません。XPagesのSSJSでも、同様です。
しかし、環境の違いを意識してコーディングするよりも、parseIntを禁止して別な手段を使ったほうが無難でしょう。
代わりに下記を使用します。
var str = '08',
    intStr1 = +str,
    intStr2 = Number(str);
console.log(intStr1); // -> 8
console.log(intStr2); // -> 8
parseIntと上記の代替例では、異なる点があります。
parseIntは数値で始まっていれば、数値以外の文字があっても数値に変換します。
一方代替例では、NaN となります。
var str = '123text',
    intStr0 = parseInt(str),
    intStr1 = +str,
    intStr2 = Number(str);
console.log(intStr0); // -> 123
console.log(intStr1); // -> NaN
console.log(intStr2); // -> NaN

まだまだつづくよ

今回は短いですが、このへんで。
次回もJavaScript。型変換についてもう少し説明します。

連載:ちょっとディープなXPages 第2-1回~JavaScriptをもっと知ろう

みなさん!XPages開発していますか!?
最近は、JavaScriptの重要性が上がってきていますね。
ご存じのとおり、XPagesでもサーバー、クライアント側ともにJavaScriptを使うのでXPagers(エックスページャーズ→XPages開発者→今作った言葉(*ノω・*)テヘ)は必携の技術です。
でも、JavaScriptってなんとなくで使ってませんか?
JavaScriptは簡単に使える言語だけでなく、高度な使い方を覚えると非常に強力で、開発効率が向上します。
また、JavaScriptでは気をつけておきたい定石もありますので、しっかり身につけておきましょう。
なお、サンプルコードはサーバーサイドJavaScript(SSJS)を想定していますが、ログの出力以外に関しては、全く同じです。

JavaScript禁忌技

まずは、JavaScriptで行なってはならないことから。やるなよ、絶対にやるなよ。

等価演算子に"=="は使わない

ifの条件分岐でよく使う等価比較演算子は、2種類あります。"=="と"==="です。"=="だけ使ってませんか?
さてその違いですが・・・実際に試してみましょう
var a = 1,
    b = "1";
print( a == b ); // true
print( a === b ); // false
この結果から分かる通り、
"=="は両端が違う型の場合は、右辺を左辺と同じ型になるように自動型変換して比較します。
"==="は、自動型変換しません。厳密等価演算というやつですね。
"=="のほうが何やら楽…と思われるかしれませんが、ソースコードを他の人や後から見たときに、この比較が型変換を意図したものかどうか、判断することができません。
型変換する場合は"=="を使わず、明示的に変換してから"==="で比較するのがマナー。
var a = 1,
    b = "1";
print( a === (+b) ); // true
型変換については後ほどまとめてやります。

配列を for-in で回さない!

配列を巡回して処理することはよくあります。で、for( var i in array) とよくやりがちですが、これはNGです。
JavaScriptにおける for...in は配列のアイテムを反復するのではなく、オブジェクトのメンバーを反復するステートメントです。
var a = ["A","B","C"];
//オブジェクトaにメソッドを追加
a.doubleJoin = function(){
    return a[0] + a[0] + a[1] + a[1] + a[2] + a[2];
}
for( var i in a){
    print(a[i]);
}
// 結果
// A
// B
// C
// function(){…}  ←想定外の出力
for...inを見たら、即座に頭の上に"!"が出てくる位に気を付けてください。
もちろん、オブジェクトのメンバーを反復する意図として使うのは可能です。
しかし、forにはまだ気を付けなくてはならないことがあります。

forの書き方によってはパフォーマンスに大きな影響あり!

↓のソースには問題があります。
for(var i=0; i < array.length; i++){
    //何らかの処理
}
このどこがいけないのか、と思うかもしれません。動作としては問題ありませんが、パフォーマンスに影響します。
問題は、2番目の項[i < array.length]にあります。この項は反復のたびに実行される部分で、array.lengthが毎回評価されるため、パフォーマンスに影響します。
array.lengthを先に確保することで回避できます。下記のように書き換えるだけで、SSJSでは処理速度に約2倍の差が出ることを確認しています。
var array = [], i = 0;

//ここで配列 arrayに大量に要素が入れられると仮定

for(var i = 0, max = a.length; i < max; i++){
    //反復ごとの処理
}
配列の要素の数が少ない場合は微々たる差ではありますが、癖をつけておくと良いでしょう。
また、コールバック関数を利用して、この反復を強制させる手段もありますが、それは後日にしたいと思います。

まだまだ続くよ

JavaScriptについてはまだまだ書き足りませんねぇ。
何回かに分けて書いていくのでお楽しみに!