mofmof.js で日本語コメントの併記を始めました

uupaa.js の開発中に、

  1. 図形を交えたり、コードの由来を説明する外部リンクをソースコードに埋め込む オンラインコメントスタイル おすすめ
  2. 独自○○ より オレオレ○○ と呼べばいいよ。オレオレライブラリとか、オレオレスタイルとか
  3. オレオレコーディングスタイルを発明せず、JS特有の事情を考慮しつつ、既存のコーディングスタイルを流用するといいよ
  4. == ではなく === おすすめ
  5. typeof hoge == "undefind" より hoge === void 0 おすすめ
  6. ナビ子記法 おすすめ

など、2008年頃は存在しても注目されてなかったり、存在もしなかった用語や用法をこさえたりと、色々やったりしてました。

uupaa.js のベースライブラリとなる、現在開発中の mofmof.js でも

  1. 日本語コメントを併記 してみたり
  2. 短さよりも、最適化と可読性のバランスを考慮したコードや、対称性を重視したコードに してみたり
  3. 経験を踏まえて、prototype拡張として実装するのが自然なものは、prototypeの下に置くべき とか考えなおしてみたり
  4. Titanium, NGCore, Windows 8 で最適化されるように ビルドオプション追加してみたり

などをやり始めています。

日本語コメントはこんな感じ

http://code.google.com/p/mofmof-js/source/browse/trunk/0.1/src/mofmof.js
はてなダイアリーだと、横幅が厳しい感じですね。

// mm.factory - Class and Nodes instance factory
function mm_factory(expr,   // @param ClassNameString/NodeQueryExpressionString/        // 引数: クラス名, Node検索文字列, Nodesインスタンス, Node, Nodeの配列
                            //        NodesInstance/Node/NodeArray:
                    arg1,   // @param Mix/Node/NodesInstance(= undefined):              // 引数: クラスインスタンス生成時の第一引数 または Nodes に渡すコンテキスト(省略可能)
                            //                          class arg1 or Nodes Context
                    arg2,   // @param Mix(= undefined): class arg2                      // 引数: クラスインスタンス生成時の第二引数(省略可能)
                    arg3,   // @param Mix(= undefined): class arg3                      // 引数: クラスインスタンス生成時の第三引数(省略可能)
                    arg4) { // @param Mix(= undefined): class arg4                      // 引数: クラスインスタンス生成時の第四引数(省略可能)
                            // @return ClassInstance/NodesInstance:                     // 戻り値: クラスインスタンス または Nodes インスタンス

    if (typeof expr === "string" && Class[expr]) {                                      // 文字列が指定され、それが mm.Class に登録済みならクラスインスタンスを生成する
        return new Class[expr](arg1, arg2, arg3, arg4);                                 // クラスインスタンスを生成し返す。適用される引数は最大で4つ
    }
    return new Nodes(expr, arg1);                                                       // Nodesインスタンスを生成し返す。適用される引数は最大で1つ
}

Prototype拡張はこんな感じに

http://code.google.com/p/mofmof-js/source/browse/trunk/0.1/src/object.js

上記のソースコードでは、Object, String, Number, Date, Array, Boolean, Function などに

  1. ECMAScript-262 5th 由来のもの
  2. Ruby 由来のもの
  3. JSON 由来のもの

を入れ込んでいます。

Prototype.js のように既存のネイティブコードを上書きするような事もありません。

// --- Object ---
// Object.keys()
// --- Array ---
// Array.isArray()      Array.toArray()     Array.range()
// Array#range()        Array#f()           Array#or()
// Array#and()          Array#fill()        Array#clean()
// Array#clear()        Array#clone()       Array#unique()
// Array#toHash()       Array#indexOf()     Array#lastIndexOf()
// Array#map()          Array#some()        Array#every()
// Array#filter()       Array#forEach()     Array#flatten()
// Array#reduce()       Array#reduceRight()
// --- Date ---
// Date.now()           Date#toJSON()
// --- Boolean ---
// Boolean#toJSON()
// --- Number ---
// Number.unique()      Number#has()        Number#pad()
// Number#clip()        Number#toJSON()     Number#inRange()
// Number#toHexString()
// --- Function ---
// Function#bind()      Function#build()
// --- String ---
// String#f()           String#has()        String#trim()
// String#trimTag()     String#trimQuote()  String#repeat()
// String#toHash()      String#toJSON()     String#sprintf()
// String#truncate()    String#toByteArray()
// --- JSON ---
// JSON.parse()         JSON.stringify()

正規表現を明記せず、RegExp#exec を String#split にするとちょっとハッピーに

JavaScript の小ネタです。

UserAgent 文字列(window.navigator.userAgent)

"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.814.0 Safari/535.1"

から、Windowsのバージョン(6.1)を取り出すようなコードをよく見かけますね。

ちょっと前の私は RegExp#exec + parseFloat で、こんな(↓)感じにしていたり、

function getWindowsVersion() {
    var m = /Windows NT (\d+(\.\d+)?)/.exec(navigator.userAgent);

    if (m) {
        return parseFloat(m[1]);
    }
    return 0;
}

こんな(↓)感じにワンライナーにしちゃってましたが、

var version = parseFloat((/Windows NT (\d+(\.\d+)?)/.exec(navigator.userAgent) || [,0])[1]);

最近は、String#split + parseFloat で、もっとシンプルにしてるよ!

var version = parseFloat(navigator.userAgent.split("Windows NT")[1]) || 0;

正規表現やめちゃって文字列に。
切り出した文字列に含まれる空白は parseFloat(" 6.1") → 6.1 って感じにトリミングされるので、parseFloat にお任せしてます。

正規表現を明記しないことで、こんなにいいことが! 的なオマケ

IE10 からは、UserAgent に埋まっている Version 番号が二桁になりますね。

"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)" 

ブラウザのバージョン番号が二桁になるシーズンになると、各ブラウザベンダーのブログ(過去においてはIEOpera のブログ)(おそらくは来年の Firefox のブログ)には、
「バージョン番号の取得に /MSIE (\d\.\d)/ のような一桁の数字しか取得できないコードが埋まってると、間違ったバージョンが取得されちゃうかもしれない…」なエントリが乗ったりするのですが、

String#split() + parseFloat で取得するようにすれば、いいんじゃないかな!? って思います!

Opera の userAgent に謎の Version番号(9.80) が埋まってるのも、これ(↑)が理由なんだよね
"Opera/9.80 (Windows NT 6.1; U; ja) Presto/2.9.168 Version/11.50"

HTML5の文字エンコーディングの判定は 512byte または 1024byte 以内までに

uupaa2011-07-10


HTML5のWorking Draft(20110113)までは、文字エンコーディング判定の範囲はファイルの先頭512byte以内と定義されていました。

そのため、

<!DOCTYPE html>
<!--[if IE 8 ]><html lang="ja" xmlns:og="http://ogp.me/ns#" xmlns:mixi="http://mixi-platform.com/ns#" xmlns:fb="http://www.facebook.com/2008/fbml" class="ie8"><![endif]-->
<!--[if IE 9 ]><html lang="ja" xmlns:og="http://ogp.me/ns#" xmlns:mixi="http://mixi-platform.com/ns#" xmlns:fb="http://www.facebook.com/2008/fbml" class="ie9"><![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--><html lang="ja" xmlns:og="http://ogp.me/ns#" xmlns:mixi="http://mixi-platform.com/ns#" xmlns:fb="http://www.facebook.com/2008/fbml"><!--<![endif]-->
<head>

といった、546byteの文字列の後に

<meta charset="UTF-8">
<title>こんにちは!こんにちは!</title>

とやってしまうと、正しく判定されるのか? ちょっと危うい気がしてしまいます。

http://www.w3.org/TR/2011/WD-html5-diff-20110405/ に書かれているように、
現在では、「512byte」→「1024byte」に改訂されています。
この時期に登場した IE9 はどちらの仕様でビルドされているのでしょうか? ちょっと気になりますね。

また、IE10pp2 からは ↓ のようなコンディショナルコメントは利用できなくなります(スルーされます)。

<!--[if IE 8 ]> <![endif]-->

ここ大事ですので、忘れないようにしましょう。


2011-07-10追記
IE9.01 は 512byte なんだろうか、それとも昔のIEのように、もっと先まで読んで判別しているのだろうか? という所が知りたくて、EUC-JP なファイル等も作ってみたのですが、文字化けしている状態でユーザが正しいエンコードを指定しなおすと、それ以降エンコードのパターンを学習する(またはエンコードに利用するバリエーションが増える)ような動きがあるらしく(その動きもあって、こちらの予想とは違う結果が出てしまう)、なんだかモヤモヤしたままです。

添付画像は、IE9.01 で http://jp.msn.com を開き、エンコードを[日本語 (自動選択)] にしたら文字化けしたよ の図

パーティクル3万個

uupaa2011-06-22


IE 10pp2 と iPad2 (iOS5) のスコアを追記しました
iPhone4S (iOS5) のスコアを追記しました

Firefox 4 から window.mozRequestAnimationFrame が利用可能になっています。

window.requestAnimationFrame / setTimeout(,4) / setInterval(, 16) で パーティクル3万個を飛ばすデモ

ベンチマークマシンのスペック → MacBook (13-inch, Late 2009) - 技術仕様
メモリ4GBに + BootCamp + Windows7 上で計測しています。

Windows エクスペリエンスインデックスの数値は、5.1 で、各項目は

  • プロセッサ: 5.8
  • メモリ: 5.8
  • グラフィックス: 5.1
  • ゲーム用グラフィックス: 5.5
  • プライマリハードディスク: 5.9

となっています。

各ブラウザのスコアはこちら

requestAnimationFrame または setInterval(16)
Browser fps method
IE 9.0.1 (x86) 45 setInterval(, 16)
IE 9.0.1 (x64) 17 setInterval(, 16)
IE 10 pp1 45 setInterval(, 16)
IE 10 pp2 44 msRequestAnimationFrame
IE 10 pp2 44 setInterval(, 16)
Safari 5.1 beta 50 setInterval(, 16)
Firefox 5 60 mozRequestAnimationFrame
Chrome 14 60 webkitRequestAnimationFrame
Opera 11.11 62.5 setInterval(, 16)
iPhone 3GS (iOS4.3.3) 5.2 setInterval(, 16)
iPhone 4GS (iOS5) 14.1 setInterval(, 16)
iPad 2 (iOS4.3.3) 12.9 setInterval(, 16)
iPad 2 (iOS5.0) 17.6 setInterval(, 16)
Firefox 5 (DHD) 20.5 mozRequestAnimationFrame
MobileWebKit 533.1 (DHD) 3.9 setInterval(, 16)
MobileWebKit 533.1 (GP) 5.2 setInterval(, 16)
setTimeout(4) で実行
Browser fps requestAnimationFrame または setInterval(16)と比較して
IE 9.0.1 (x86) 41 やや遅くなる
IE 9.0.1 (x64) 16.5 やや遅くなる
IE 10 pp1 42.5 やや遅くなる
Safari 5.1 beta 43 やや遅くなる
Firefox 5 100 とても速くなる(速くなりすぎる?)
Chrome 14 60 変化なし
Opera 11.11 95 とても速くなる(速くなりすぎる?)
iPhone 3GS (iOS4.3.3) 5.1 変化なし
iPhone 4GS (iOS5.0) 13.5 やや遅くなる
iPad 2 (iOS4.3.3) 12.9 変化なし
iPad 2 (iOS5.0) 16.4 やや遅くなる
Firefox 5 - (DHD) 20.2 やや遅くなる
MobileWebKit 533.1 - (DHD) 5.2 やや速くなる
MobileWebKit 533.1 - (GP) 5.2 変化なし

※DHD = HTC Desire HD (Android OS 2.2)
※GP = GALAPAGOS SoftBank 005SH (Android OS 2.2.1)

このテストでわかったことは、
・MobileWebKit 533.1 で動かすとパーティクルが緑色で表示される(バグかな?)
・setTimeout(4)はsetInterval(16)に比べ負荷が高くなる傾向がある
Firefox 5 と Opera 11 は setTimeout(4) に追従している。未来を感じる
iOS 5.0 の MobileSafari は window.webkitRequestAnimationFrame() 未実装

IE 9の64bit版は今回もパフォーマンスチューニングなしでリリースされている

IE 9.0.1 (x86)に比べ、x64版が極端に遅いため、x64版は今回もチューニングせず出荷されたようです。x64版はこのまま「時間切れで出荷された可哀想なブラウザ」なのでしょうか?

Number.prototype.toHexString

数値(num)から4桁のゼロ埋めされた16進文字列が欲しい場合に

var num = 123;
var rv = ("000" + num.toString(16)).slice(-4);

alert( rv ); // "007b"

と毎回書くのもアレなので、こんな感じに書けるといいんじゃないでしょうか。

alert( (123).toHexString(4) ); // "007b"

実装してみました。

//{@prototype
(function(_DEC2HEX) {

// create HEX2 cache
for (var i = 0x100; i < 0x200; ++i) {
    _DEC2HEX[i - 0x100] = i.toString(16).slice(-2);
}

Number.prototype.toHexString = function(n) { // @param Number(= 2): digits. 1 ~ 8
                                             // @return String: HexDigitString
    //  [1][omit arg]   console.log(   (1).toHexString()  ) -> "01"
    //  [2][n = 1]      console.log(   (1).toHexString(1) ) ->  "1"
    //  [3][n = 2]      console.log( (255).toHexString(2) ) -> "ff"
    //  [4][n = 8]      console.log( (255).toHexString(8) ) -> "000000ff"
    //  [5][Math.floor] console.log( (1.1).toHexString(2) ) -> "01"
    //  [6][Math.abs]   console.log(  (-1).toHexString(2) ) -> "01"

    var val = (this < 0 ? -this : this) | 0,
        digit = (n === void 0 || n <= 0) ? 2 : n;

    if (digit === 2) {
        return _DEC2HEX[val & 0xff]; // use cached string
    }
    return ("0000000" + val.toString(16)).slice(-digit);
};

})(Number.prototype._DEC2HEX = {});
//}@prototype

小数点以下を切り捨て、負の数値は正の数値として処理した16進数文字列を返します。
先頭に"0x"は付きません。toHexString(n) の n で指定された長さまでゼロフィルを行います。
結果の文字列の長さが n より長い場合は、下 n 桁の文字列を返します(上位の桁を切り落とします)。n は省略可能です。省略した場合は n = 2 を指定した場合と同じ結果になります。n に 1以下の値や無効な値を指定すると n = 2 と同じ結果になります。

n = 2 の場合に限り、予め作り貯めておいた Hash ( Number.prototype._DEC2HEX ) から文字列を検索し、わりと素早く結果を返す気がします。
あと、Number.prototype._DEC2HEX は他の処理からも使いまわす予定なので、Number.prototype にくっつけてます。

日本語の説明を読むよりも、コードを読んだほうが簡単ですね!

Number.prototype.toHexString - jsdo.it - share JavaScript, HTML5 and CSS

IE9 日本語版 非公式ダウンロード

追記: 2011-04-26 00:00 より日本語版が正式にリリースされるとのこと

Internet Explorer(R) 9 日本語版の提供日について
2011 年 4 月 6 日

 日本マイクロソフトは、このたびの震災に伴い、重要な通信インフラとなっているネットワーク回線への負荷軽減への配慮などから、 3 月 15 日(火)午後 1 時(日本時間)に予定されていた最新ブラウザーInternet Explorer(R) 9 日本語版の製品版提供の延期をしておりましたが、このたび新たな提供日を 4 月 26 日(火)午前 0 時(日本時間)と決定しましたのでご案内申し上げます。なお、ネットワーク回線への負荷軽減のため、同日はダウンロード提供のみで、当初同日実施を予定していたベータ版・製品候補版ユーザー向けの自動更新については後日段階的に実施する予定です。

via http://www.microsoft.com/japan/presspass/news/default.aspx#110406


震災の影響で、IE9 日本語版の正式リリースは残念ながら延期されていますが、ファイルは既に存在するとのこと。

IE9 を入れると IE8 環境が無くなってしまいますが、IE8 互換モードで十分にカバーできるため、インストールできる人は躊躇せず入れてしまうべきです。
# 私は Windows7 32bit, 64bit, Windows Vista 32bit の計3台の環境を IE8 から IE9 にしました
# Vista Ready PC(3〜4年ものの非力なPC)でも、IE9 はちゃんとキビキビと動いてくれていました

IE9 が出たから、二つ前のバージョンの IE7 と IE6 にはお引き取り願わないとね!

IE9 はリリース後のわずか一日でブラウザシェアの0.8%をモギリ取ったようです(in 北米)
http://gs.statcounter.com/#browser_version-na-daily-20110315-20110316-bar

Google Chrome、1日で1%のシェア獲得 - ITmedia NEWS といい勝負なんではないでしょうか。

DOM Lv0 イベントハンドラ内で media 変数にアクセスできない

IE6〜IE9RC では、 DOM Lv0 イベントハンドラ内では media 変数にアクセスできないようです。謎です。

var media = "(ε・◇・)з はろー";

<!-- IE6〜IE9RCで動作しないケース -->
<input type="button"
    value="IEだとmedia変数が見えないよ"
    onclick="alert(media)" />
    
<!-- window.media と明示すれば大丈夫そそうだけど… -->
<input type="button"
    value="window.media変数は見えるよ"
    onclick="alert(window.media)" />

消えるmedia変数 - jsdo.it - share JavaScript, HTML5 and CSS