昔書いた社内ブログ記事です。
※当時のPHPには無名関数とかありませんでした。
おひさしぶりこ(p・ω・q)
『JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス』を読み終わりました。
今まで気にしてなかったことや、新しく分かったことのうちいくつかをメモっておきます。
1. ループに名前がつけられる
ループの中にループが入れ子することって、ありますよね。
何階層か遡ってループから抜け出すときに、PHPだと
break 2; // 何も指定しないと1
のように数字で指定できますが、
JavaScriptでは名前で指定できるようです。
myWhile : while(true) {
for(var i = 1; i > 0; i++) {
if(i > 10) {
break myWhile;
}
}
alert("[IN] i = "+i); // 実行されない
}
alert("[OUT] i = "+i); // 実行される
2. undefinedなオブジェクトでエラーを避ける方法
PHPでは、下のように存在しない配列 $myFamily["brother"]
のさらに
下のプロパティ(この場合は name
)を指定してもエラーにはなりません。
$myFamily = array(
"father" => array("name" => "ヴィントン・サーフ"),
"mother" => array("name" => "グレース・マリー・ホッパー")
);
echo $myFamily["father"]["name"]; // ヴィントン・サーフ
echo $myFamily["brother"]["name"]; // NULL
JavaScriptではエラーになるので厄介です(p´へ`q)
この場合、&&演算子を使ってエラーを回避するようです。
var myFamily = {
"father" : {"name" : "ヴィントン・サーフ"},
"mother" : {"name" : "グレース・マリー・ホッパー"}
};
alert(myFamily.father.name); // ヴィントン・サーフ
alert(myFamily.brother.name); // 'myFamily.brother.name' は Null またはオブジェクトではありません。
alert(myFamily.brother && myFamily.brother.name); // undefined
ってことはもっと深い階層のものまで考慮したら、
alert(myFamily && myFamily.brother && myFamily.brother.name && myFamily.brother.name.firstName);
とかなるのかなー(イヤダナー。笑)。
あ、最低限トップレベルのmyFamilyは宣言されていないとエラーになるみたいなので、最初の「myFamily &&」は不要ですね。
3. ループの中で作成した要素それぞれにクリックイベントを付与する方法
少し前にこれで悩んだことで、JavaScriptを勉強しなおそうと思ったのでした。
1から5までのボタンを作り、クリックしたらその番号をアラートするとします。
(jQueryの方が慣れているのでそれで書きます・・)
これは以前やっちゃった失敗例。
すべてのボタンで、6(最終的なiの値)がアラートされますOrz
$(function(){
for(var i = 1; i <= 5; i++) {
$("<input />", {type : "button", value : i})
.appendTo("body")
.click(function(){
alert(i); // すべて6
});
}
});
こういう場合は、ループ中のある時点でのiの値を保持するために、
無名関数を作ってその中で値をreturnしてあげると成功します。
$(function(){
for(var i = 1; i <= 5; i++) {
$("<input />", {type : "button", value : i})
.appendTo("body")
.click(function(i){
return function(){
alert(i); // それぞれのiの値が確保される
};
}(i));
}
});
function(){}
だと関数そのものを、function(){}()
だとreturnされた結果を参照するんですって!
なんかこれは、いろんなところで使えました。
$(なんとか).each(function(){...});
とか。
4. 配列かどうかを判定する方法が意外とむずかしい
JavaScriptではこういう風に一筋縄ではいかないらしい。
if(is_array($arArray)) {
echo "配列です";
};
なんでもオブジェクトと配列の区別がつきにくいようですね。
私には文字列とオブジェクトの区別もよくわからんですよ。
alert("あいうえお".length);
なにこれ。笑
今はもう慣れましたが、これで「あいうえお」の文字数を
取得してるというのが感覚的にピンときませんでした。
どう見ても「あいうえお」とlength変数の連結でs
というか文字列も配列も関数もみんなオブジェクトみたいなので仕方が無い。
個人的にはJavaScriptの関数の使い方は気に入りましたけど。
神出鬼没な感じが(笑)
本題から離れてしまいましたが、
著者がお勧めしている配列の判定方法はこちら。
var is_array = function(value) {
return value && // nullなどをはじく。
typeof value === 'object' && // objectと配列にしぼる(nullも入るが上で除外ずみ)。
typeof value.length === 'number' && // 配列ならtrue。objectは不明。
typeof value.splice === 'function' && // 同上。
!(value.propertyIsEnumerable('length'));// for inループで取り出せるかどうか。配列ならfalse。
}
なかなか複雑ですねΣ(´д`∴)
5. nullの型はなぜかobject
変数○○がオブジェクトならば~って処理を書くときに、
知らぬ間に通過してくるのがnullだそうです。
var myNull = null;
if(typeof myNull === 'object') {
alert("objectだよ!");
}
null以外のオブジェクトを拾いたい場合は以下のようにしなくてはいけない。
var myNull = null;
if(myNull && typeof myNull === 'object') {
alert("objectだよ!");
}
else {
alert("nullだよ!");
}
6. parseIntは基数をちゃんと指定したほうがいい
Int型変換を行うparseIntですが、数字の前方に「0」がついていると
8進数と解釈してしまっておかしくなるようです。
alert(parseInt("1")); // 1
alert(parseInt("010")); // 8(8進数になる)
alert(parseInt("010", 10)); // 10(10進数を指定する)
日付や時刻はゼロ埋めしていることが多いので、
日ごろから基数を指定するようにするとぐっど。
7. 0.1 + 0.2 = 0.3にならない
まぁPHPもそうですが、小数点の計算は面倒ですよねw
mならcmに、セントならドルに、整数に直してから計算すると
正しい結果が得られまする。
alert(0.1+0.2); // 0.30000000000000004
alert((0.1*10 + 0.2*10)/10); // 0.3
8. 1 + 2 = 12になることがある
PHPなら文字列の連結には「.」を使うので、
「"1" + "2"」の場合でも答えは3になってくれるんですけどね。
JavaScriptの場合はInt型に変換する癖をつけた方がよさそうです。
alert(1 + 2); // 3
alert("1" + "2"); // 12
alert(parseInt("1", 10) + parseInt("2", 10)); // 3
9. function文よりfunction式が推奨される
// function文
function getRoot(n) {
return Math.sqrt(n);
}
// function式
var getRoot = function(n) {
return Math.sqrt(n);
}
後者の方が、宣言する場所が限定されるため、コードがスクランブルになりにくいのと、
関数が値であることが顕著であるため。
10. newはなるべく使わない
newを忘れるととんでもないことになるため、
そもそもnewを使わなくてもいい実装にするべきだというのが著者の考え。
おまけ
undefinedとNaNはグローバル変数なので、値を変更することができる
恐ろしい!!
ちなみにhasOwnPropertyなども上書きできてしまう。おそろいい。
他にもカリー化とかメモ化とか使ったことがないものがたくさんありました。
まだまだ知らぬことばかりですね。
ではまた。