JavaScriptのfor/forEach/jQuery.each/Ext.each のパフォーマンスを計ってみた。

先日、Sencha Touch 2 ソースコード読書会 第2回@東京でナビゲータを務めさせて頂きました。その時の様子は以下などに。

この中で、Array.forEachと Ext.each のどちらがどのくらい速いのか、というのが話題に出ました。そういえば昔、JS ArrayのforEach, filter, map の速度を調べてみた。というエントリで調べました。あれから1年くらい立ってるんだけど、最近はどうなのかまた調べてみました。

jsfiddle を使って、生のfor文、Array.forEach、Ext.each、jQuery.each の比較ができるようにしました。
http://jsfiddle.net/dsuket/DR33Q/

配列に0から指定数まで数字を入れて、ループでそれらを足し合わせる計算時間を測定しています。私の環境(iMac Early 2009 )だと 1000万回くらいから差が出てきました。以下は OS X 10.8.3/Chromeで、各五回実行した最小の測定結果。単位は ミリ秒。

Chrome 27.0.1453.93

回数 for forEach jQuery.each Ext.each
100万 0 7 11 10
1000万 2 106 156 149
1億 20 762 1284 1189

for文が圧倒的に速いですね。jQuery や Ext の each は ネイティブの forEach に比べると 60%程遅いようです。あと、なぜか jQuery より Ext のほうが何回やってもちょっとだけ速い!

FireFoxSafariでもやってみました。

FireFox 21.0

回数 for forEach jQuery.each Ext.each
100万 0 2 4 2
1000万 3 15 30 15
1億 51 138 322 141

Safari 6.0.4 (8536.29.13)

回数 for forEach jQuery.each Ext.each
100万 6 27 22 25
1000万 68 265 256 257
1億 680 2915 2460 2507

FireFox 速い!forEach がかなり速いですね。Ext.eachも負けないくらい速いです。それに比べ、Safari はループが遅い上に、初期化もすごく時間かかりました。さらに、Safariにはとんでもないバグがあることがわかりました。。

その話はまた次のエントリで。
→ 書きました! JavaScriptのfor/forEach/jQuery.each/Ext.each のパフォーマンスを計ってみた。

gruntで更新されたファイルを対象にタスクを動かす方法

JavaScript のビルドツール grunt が熱いということで使い始めました。

Sencha Touchの場合は senchaコマンドでビルドできるのですが、それ以外のプロジェクトでリリースビルドを作るのに、sassのコンパイル、cssのminify、jsの結合とminify、そしてリリースディレクトリへのコピーなどを一括で行うために使っています。

便利なんだけど、毎回全ビルドが走るのがどうにも非効率だなぁ、と感じていました。更新されたファイルだけ対象にしてくれればいいのに。というかantとかrakeとか他のビルドツールには普通あるでしょ!

同じような事を思っている人は当然いるわけで、gruntに提案されています。しかしまだ取り込まれていないようです。→ Provide conditional support for file inclusion

ここでも言われてますが、gruntのタスクに定義するファイルには filter オプションというのがあって、タスク実行時にsrcファイルを任意の関数でフィルタリングすることができます。これを使えば、更新されたファイルだけ対象することができそうです。

と、いうわけで作ってみた。dsuket/Gruntfile.js

3つのフィルタ関数を作りました。

  • newer
    • srcがdestよりも新しければ true を返す。
  • expandNewer
    • destにディレクトリを指定して、srcの各ファイルと比較する。
  • newerAny
    • srcの何れかがdescよりも新しければ true を返す。

grunt のカスタムフィルタ関数は、タスク実行前に srcファイルを引数に呼ばれます。true を返せばそのファイルを対象とし、falseならば対象としません。Custom Filter Function

フィルタ関数の引数には srcしか渡されないため、destファイルがわかりません。そのため、フィルタ関数を返す関数を作成し、その引数に dest ファイルを指定するようにしました。やや冗長ですが仕方ない。

悩ましいのが、newerAny。フィルタ関数は1つのsrcファイル毎に呼び出されるため、予め全てのsrcがわかりません。そこで第2引数にsrcの定義を取るようにしました。配列でも定義できますが、文字列で与えた場合、その変数の値を取ります。(あんまりいけてない)

大体使えるようになりましたが、まだ問題はいくつかあります。

テンプレートが使えない。

テンプレートの展開は grunt.initConfig() の中で行われるので、newerのターゲットでテンプレートを使っても展開されません。

watch で newerAny が更新されない

最初のsrcファイルのフィルタ実行時にしか判定しないため、watchで起動したときは更新されません。毎回チェックするようにすればいいんですが、今のところwatchはあまり使っていないのでペンディング。

newerAny で expand が使えない。

srcファイルの展開は実行時行われるため、定義時にsrcの一覧が取得できません。


と、まぁ幾つか細かい問題がありますが、このフィルタの最大の問題点は、uglify で失敗するということです。

grunt-contrib-uglify タスクが、srcファイルが無い場合エラーで止まる問題があります。filterの結果、更新ファイルがなければ src は空になるのですが、その場合以降のタスクが止まってしまいます。他のタスクはsrcが空でも動くようになっているので、grunt-contrib-uglifyのバグだと思います。一応修正版を github にアップしました。(pull requestも出しておいた)
https://github.com/dsuket/grunt-contrib-uglify


参考:

iOS/Android で HTML5 の audio/video を任意のタイミングで再生する方法

HTML5 で追加された audio/video 属性によって、プラグインレスで動画や音声の再生が可能になったことはよく知られていますが、モバイル(スマホ)で、その再生タイミングの制約が厳しいことは案外知られていません。これは以前、当Blogでも HTML5 x Touch UI の UXを考える(補足) の User action event restrictions でもチラッと書きました。またしてもこの制約に苦しめられながら、なんとか解決策を見いだしたので、GW最中にも関わらず久しぶりのエントリーです。

まず、本家 Apple のドキュメントにも以下のようにあります。
iOS-Specific Considerations

In Safari on iOS (for all devices, including iPad), where the user may be on a cellular network and be charged per data unit, preload and autoplay are disabled. No data is loaded until the user initiates it. This means the JavaScript play() and load() methods are also inactive until the user initiates playback, unless the play() or load() method is triggered by user action. In other words, a user-initiated Play button works, but an onLoad="play()" event does not.

(超意訳)

iOS の Safari では、ユーザーが携帯網で重量課金されるかもしれないから、preload と autoplay は効かないよ。ユーザーが何かアクションしないと、JavaScript での play() や load() もダメ。つまり、再生ボタンを押して play() を呼べるけど、onload で play() しても無効だから。

このユーザーアクションというのが結構厳しい。touch/mouseイベントの直接のハンドラ内でしか効きません。setTimeoutなどのタイマーを挟むともうアウトです。

例えば、以下のようなコードは再生できます。http://jsfiddle.net/dsuket/jTh3v/

var playBtn = document.getElementById('play');
playBtn.addEventListener('click', function() {
    audio.play();
}, false);

しかし、これにタイマー挟むと再生されません。 http://jsfiddle.net/dsuket/Ny9Xz/ (PCブラウザでは再生できます)

var playBtn = document.getElementById('play');
playBtn.addEventListener('click', function() {
    setTimeout(function(){
        audio.play();
    }, 2000);
}, false);

これは setTimeout だけじゃなく、例えば transitionEnd などのアニメーション関連のイベントなどもダメです。そうすると、アニメーション終了時のタイミングで音声を再生したい場合に非常に困る。

そこで、以下のようにすると再生できます。http://jsfiddle.net/dsuket/rWpM7/

var playBtn = document.getElementById('play');
playBtn.addEventListener('click', function() {
    audio.load();
    setTimeout(function(){
        audio.play();
    }, 2000);
}, false);

ポイントは、setTimeout の前に load() することです。play() メソッドは内部で自動的に load が呼ばれるのですが、タイマーハンドラの中ではこの load が抑止されているようです。そこで、ユーザーイベントが発生した段階ですぐに load() だけ呼び出し、後は任意のタイミングで play() すればOK!

なお、ドキュメントonload のタイミングで自動再生したい、というのはやはりできません。そこで、少し画面フローを工夫して、最初にスプラッシュ画面を用意し、タップしてもらうようにします。そのタップイベントハンドラの中で、オーディオファイルをロードします。HTML5 Webアプリでよく使われる Single Page Applicationの場合、ここで必要なオーディオファイルを全部ロードしてしまうと良さそうです。そうすれば後は好きなタイミングで音声を再生できます。

audioについての説明でしたが、videoも同様の問題があります。検証できてませんが、同じようにすれば対応できそうです。

ただ、audio/videoの自動再生や再生タイミングはOSバージョンでコロコロかわります。そもそも iOS4.1、Android 2.3 までは setTimeout の中でも再生できました。それが iOS 5、Andoroid 4 あたりから再生できなくなりました。バージョンが違えばこの load を切り離す方法もまた使えなくなるかもしれません。十分検証には気をつけてください。


参考:

Sencha Touchパーフェクトガイド が出版されました!

年末年始に書いていた本「HTML5モバイルアプリケーションフレームワーク Sencha Touchパーフェクトガイド 」が遂に出版されました!

共著で私は主に7章、8章あたりを担当させていただきました。メイン担当はありますが、もちろん他の部分もレビューし、全体的に参加しています。

Sencha Touch 2についての本は意外と少なく(日本語書籍では2冊目)、これだけのボリュームのある書籍は世界でも希です。(現時点では)まさにパーフェクトといえる内容だと自負しています。

特に3部の実践では、実務で役に立つ内容がてんこ盛り!実際に使った人が悩むポイントやはまり所などが詳しく解説されています。私も非常に勉強になりました^^;

しかし、まだまだ書き足りないと思うところがあるのも事実。。特に Ext のユーティリティメソッドに関してはほとんど触れられなかったのが少し残念です。(そのあたりは後述の Sencha Touch 2 Up and Running が詳しい。) また、テストやデバッグについてもあればよかったかも、とも思います。

とはいえ、最初の基本から応用、そして実践までの解説に関しては、今のところ本書が世界で最も詳しく書かれていると思います!

どのくらいボリューミーなのか、ということを他の書籍と客観的に評価してみましょう。

Sencha Touch 関連で出ている書籍で有名なのは大体以下の3つです。

id:nowokay こと、きしださんの日本で最初の Sencha Touch 2 の書籍!すごい表紙が一部で話題に!!

Sencha Forum のふざけたアイコン(w でもおなじみの Mitchell Simoens も書いている Sencha Touch 2のバイブル的存在。2への対応中で、3月末に正式出版されるらしい。現在プレビュー版を購入できる。(英語)

最近出た オライリーの Sencha Touch 2本。幅広く全体を薄く網羅している感じ。なぜかユーティリティメソッドの説明が充実 (英語)


これらの名著たちと、本書のボリュームをページ数と、容積から比較してみます。

タイトル ページ数 サイズ(cm) 容積(㎠)
Sencha Touch パーフェクトガイド 432 23.4 x 18.2 x 2.8 1192.464
Sencha Touchではじめるモバイルアプリ開発 255 20.8 x 14.8 x 2 615.68
Sencha Touch In Action 375 未定 未定
Sencha Touch 2 Up and Running 230 23.3 x 17.8 x 1.5 622.11

圧倒的ボリューム!!ページ数では2位の15%、3位の約70%増しで、容積にいたっては次点のなんと 90%増でおよそ約2倍! ページ数が膨らんだなぁ充実したなぁ、と思っていましたがここまで多かったとは。

ということで、この充実っぷり、是非一度お手にとってご確認いただければ幸いです。

充実の目次!

  • 第1部 Sencha Touch の概要
    • 第1章 イントロダクション
      • 1.1 ネイティブアプリケーションの終焉
      • 1.2 はじめよう、Sencha Touch
      • 1.3 本書の構成
      • 1.4 ソースコードのダウンロード
    • 第2章 開発準備
      • 2.1 開発準備
      • 2.2 Sencha Cmd
      • 2.3 Hello, World アプリケーション
    • 第3章 はじめての Sencha Touch
      • 3.1 モデル
      • 3.2 ビュー
      • 3.3 コントローラ
  • 第2部 Sencha Touch の基本構成
    • 第4章 クラスシステム
      • 4.1 クラス定義
      • 4.2 コンストラクタ
      • 4.3 コンフィグ
      • 4.4 インスタンス
      • 4.5 継承
      • 4.6 オーバーライド
      • 4.7 ミックスイン
      • 4.8 シングルトン
      • 4.9 静的メンバ
      • 4.10 別名
      • 4.11 依存性管理
      • 4.12 めとめ
    • 第5章 コンポーネント
    • 第6章 イベントシステム
      • 6.1 イベントシステム概要
      • 6.2 イベントリスナ
      • 6.3 リスナオプション
      • 6.4 独自イベント
      • 6.5 タッチイベント
      • 6.6 まとめ
    • 第7章 データパッケージ
      • 7.1 データパッケージ概要
      • 7.2 モデル
      • 7.3 ストア
      • 7.4 プロキシ
      • 7.5 まとめ
    • 第8章 リストとテンプレート
      • 8.1 リスト
      • 8.2 テンプレート
      • 8.3 まとめ
    • 第9章 アプリケーション
      • 9.1 アプリケーション
      • 9.2 コントローラ
      • 9.3 ルーティング
      • 9.4 プロファイル
      • 9.5 まとめ
    • 第10章 Sencha Touch デザイン概論
      • 10.1 環境の構築
      • 10.2 Sass の基本文法
      • 10.3 Compass について
      • 10.4 Sencha Touch でのデザイン変更について
      • 10.5 まとめ
    • 第11章 ネイティブパッケージング
      • 11.1 パッケージング
      • 11.2 ネイティブパッケージングの流れ
      • 11.3 iOS アプリケーションの作成について
      • 11.4 Android アプリケーションの作成
      • 11.5 ネイティブ API
      • 11.6 まとめ
  • 第3部 実践 Sencha Touch アプリケーション開発
    • 第12章 プロジェクト開始
      • 12.1 開発するアプリケーション
    • 第13章 CRUD
      • 13.1 表示(Read)
      • 13.2 作成(Create)
      • 13.3 更新(Update)
      • 13.4 削除(Delete)
      • 13.5 ルーティングとヒストリー
      • 13.6 URL の変更
    • 第14章 カスタムコンポーネント
    • 第15章 Sencha Touch Charts
      • 15.1 Sencah Touch Charts 概要
      • 15.2 図形描画の基礎
      • 15.3 グラフの基礎
      • 15.4 家計簿アプリケーションへの組み込み
    • 第16章 アプリケーションの仕上げ
      • 16.1 タブレット用画面の実装
      • 16.2 アプリケーションのリリース
      • 16.3 まとめ
  • 著者紹介

長い。さすが400ページ超えだけあって、目次も長い。

そして、奇しくも今日は自分の誕生日。
書いた本がそんな日に出版されるなんて、最高の誕生日プレゼントになりました。
ありがとうございます!

Sublime Text 2 をちょっと便利に使うTips

今さらながら Sublime Text 2 をちょっと便利に使うTipsを2つほど。

Expand Selection の スキップ

Sublime Text の特徴の一つに、Ctrl-D とかでどんどん単語を選択して、マルチセレクトで一括編集できる Expand Selection というものがあります。これはとても便利なのだけれど、特定の箇所はスキップしたい、ということも良くあります。

例えば、次のようなコードで、 "hoge_name" と "fuga_name" を、それぞれ "hoge_nickname" と "fuga_nickname" に変えたいとします。

var name_1 = "hoge_name";
var name_2 = "fuga_name";

"hoge_name" の "name" 部分を選択して Ctrl-D すればいいじゃん、と思いますが、そうすると name_2 の name にも引っ掛かってしまいます。そこは変えたくない。次の fuga_name に行きたいんです。

そういうときは、Ctrl-K, Ctrl-D でスキップできます。

また、間違えて選択した場合は、Ctrl-U で選択を一つ前へ戻せます。正確に言うと、Ctrl-U は選択状態を含め、カーソル位置を前へ戻すのでその他にも便利に使えます。

参考:http://stackoverflow.com/questions/11548308/skip-ctrld-selection-in-sublime-text-2

その他 複数選択に関するTipsが沢山! Sublime Text2の複数カーソルの使い方
(覚えきれない。。。)

特定ファイル、フォルダをファイルリストに表示させない

サイドバーにフォルダを追加して、プロジェクトとすると、Ctrl-P でファイルが切り替えられて大変便利です。が、プロジェクトが大きくなってくると、キャッシュファイルやゴミファイルなんかも出てきて鬱陶しいこともあります。

そんな時は、設定で以下を追記しましょう。全体設定でも良いですが、User Settings に書くのが良いと思います。Ctrl + , でユーザー設定が開くので、以下を追記します。

    // 除外するフォルダ
    "folder_exclude_patterns": [".svn", ".git", ".hg", "CVS"],

    // 除外するファイル
    "file_exclude_patterns": ["*.pyc", "*.pyo", "*.exe", "*.dll", "*.obj","*.o", "*.a", "*.lib", "*.so", "*.dylib", "*.ncb", "*.sdf", "*.suo", "*.pdb", "*.idb", ".DS_Store", "*.class", "*.psd", "*.db"],

    // サイドバーには表示するけど、Ctrl-Pでは表示しないファイル
    "binary_file_patterns": ["*.jpg", "*.jpeg", "*.png", "*.gif", "*.ttf", "*.tga", "*.dds", "*.ico", "*.eot", "*.pdf", "*.swf", "*.jar", "*.zip"],

上記は標準のデフォルト設定です。これに、自分の好みの設定を追記します。例えば、Compass/Sassなんかを使ってると、.sass-cache フォルダが邪魔なので、folder_exclude_patterns へ ".sass-cache" を追加すると、ファイルリストに出てこずにすっきり!

Ctrl-P では表示したくないけど、サイドバーに表示したいときは、binary_file_patterns へ追加してください。

日本語入力の問題

Windows では インライン入力出来ないとか、Emmetでは変換確定できないとか、Tabキーで変換できないなどなど、日本語入力周りは問題が多々あります。

自分の環境で困るのが、検索バーで日本語変換がうまくできないこと。カーソルキーやTabキー、EnterキーをSublime Text本体がハンドリングしてしまって、うまく変換できません。。Tabのキーバインドを無効にして変換する方法(http://d.hatena.ne.jp/kaz_shu/20120718/1342623780)もありますが、各種スニペットが使えないも痛い。それにカーソルやEnterの問題は解決していない。

こんな投票があるので、是非みなさんも一票を!!
http://sublimetext.userecho.com/topic/52165-unable-to-finalize-kana-kanji-conversion-in-japanese-in-the-search-box/

デザイナのためのGit入門 のスライドをアップしました

お世話になってるデザイン事務所で、最近よく聞くgitってやつについて教えてよ、ってことで話しました。

デザイナさん向け中心に、まずはイメージを掴んでもらう概要の説明となってます。コマンドとか意図的に載せてません。それは参考資料見てください。

30分前に急いで作ったので間に合わなかったとかじゃないから!(>_<)

話して驚いたのが、結構みんな git = github だと思ってたということ!githubの省略だと思ってたとかw

Subversionでいいじゃん、という話しもチラホラ。確かに現状困ってないのなら Subversionでも全然よいかもしれません。個人的に思うチーム開発でSubversion使ってて困ることは、どうしてもコミット単位が大きくなりがちなこと。他の人に影響与えるといけないので切りの良いところでコミットしないといけないのだけれど、なかなか切りのよい感じにならなくてどんどん修正範囲膨らんで、1回のコミットが巨大になるとか。その点gitだとローカルリポジトリへのコミットは気兼ね無しにできて良いですね。取り消しやコミットの修正も簡単だし。切りの良いところでpushすればいい。もちろん、巨大なpushはマージが大変になるのでお勧めできませんが。

あと、デザイナさんはやっぱりPSDとかaiファイルのバージョン管理をしたい、という話がありました。大きなプロジェクトになると1つのPSDファイルが数百Mになることも、とか。調べてみると、こんなサービスあるようです。「もうPSDは無くさない!デザイナー版GitHubのPixelapseはうっかりデザイナーの救世主!」 面白そう。使ってみた感想ききたい!


コマンドや個々の使い方の良い資料はたくさんあるので、それを実際どう運用するか、特に社内での運用方法、というのを次回あればやりたいですね。

てか、誰かやってください!
人の発表を聞くだけよりも、自分で調べ手を動かしながら考えた方が絶対に身につきます。それを私がやるのはもったいない。是非つぎは誰かがやってくれると期待してます。

今年もよろしくお願いします

明けてもう7日が経ちましたが、新年おめでとうございます。

ほんとは、もっと今年の抱負とか目標とかまとまってから書こうと思ってたのですが、それを待っていると年越してしまいそうなので、見切り発射ながらエントリーを投入している次第でございます。

まだはっきりと目標とまでは言えませんが、それでもおぼろげながらはやりたいことが出てきました。

まず、やはりちゃんと情報発信していきたいな、ということ。twitterfacebookのライトなポストも手軽では良いのですが、もう少しまとまった情報を整理してBlogのエントリーとして書いていきたいと思っています。単に新しい情報をうわべだけ書き連ねるのではなく、自分なりに整理した内容を書ければいいなぁ、という所存でございます。

これには狙いが2点あって、そういう意気込みで書くからにはそれなりに調べたり勉強しないといけないわけで、自分のスキルアップに直結するということ。2点目はBlogでも書き続ければ文章体力がつきそうかな、ということ。完結で説得力がある文章を短時間でかけるようになりたい、という思惑もあります。

次に、なんだかんだでやっぱり英語をちゃんと勉強したい。技術文書なんかはなんとなく読めはするけど、話すのと書くのは全然ダメ。これはほんと実践するしかないんだろうな。周りもよくやってるオンライン英会話でも始めようかな。んで、できれば6月の Sencha Conferenceに行きたい。せっかく Complete も買ったしね−。ちょうどあと半年くらいなので、目標にすると励みになってよいかな、と、割と本気です。

仕事面では、昨年からやっているフロントエンドの技術支援という形をもう少し広げたいな、という思いがあります。自分もまだまだなところも多いですが、あちこちの現場を見ているとかなり課題も多く、こうすれば簡単にできますよ、というのも結構ある。特に、最近若手と話すことも増えましたが、教えたいことも色々とあるような気がしてきました。

自分一人で開発するのはある意味、気は楽なのですが限界もある。チーム全体でうまくいくように支援したい、という思いが増えてきました。しかし、そこをどう仕事にするかが課題。

それも含めてBlogでも書いていければなぁ、というあたりで今年の抱負としたいと思います。

みなさま 今年もよろしくお願いいたします。