node.js + expressを調べてみた

触ってみたかったnode.jsを少しだけ触ってみた。

学んだことを箇条書きに

  • exports.hogehogeで設定したものはrequire()で変数(hugahuga)に指定してhugahuga.hogehogeみたいに使える
  • require()で指定するファイルは拡張子(.js)を省略できる。フォルダ名で指定した場合はindex.jsをdefaultで取得する模様
  • app.get()などでは正規表現でマッチするかを判別できる。数字だけならこんな感じ([0-9]+)
  • res.renderも同じように拡張子とか指定できる。
  • res.renderで取得するフォルダのデフォルトはapp.set(‘views’, hogehoge)で指定された場所
  • viewに指定するEngineはejs使ってるサンプルが多いけど僕はjadeが好き。

ぐらい。もうちょっと勉強が必要。

TypeScriptを触ってみた

TypeScriptを触ってみたので覚書。結構良さげ。

環境構築

最近sublime text2使ってるからyanzmさんが書いてるのを参考に環境構築。

上記エントリーには書いてないけど、以下のコマンドでTypeScriptをインストールするのも忘れずに。

sudo npm install -g typescript

書き方を学ぶ

以下のエントリーたちが参考になった。

phyzkit.net -&nbspphyzkit リソースおよび情報
phyzkit.net は、あなたがお探しの情報の全ての最新かつ最適なソースです。一般トピックからここから検索できる内容は、phyzkit.netが全てとなります。あなたがお探しの内容が見つかることを願っています!
phyzkit.net
 

大体の仕様を把握したら本家からSampleCodeをダウンロードしてきて動かしてみるといいと思う。

他のファイルを参照

TypeScriptでjsをrequireするような機能はない。tsファイルをrequireしかできないみたい。他で定義されているtsファイルを使いたい場合は以下のように記述する。jQueryとか使いたい場合はこの機能でtsファイルに変換されたjQueryをrefarenceで持ってくることで使える

/// <reference path="hello.ts"/>

まとめて1ファイルを作る

自分で作っているtsファイルなのであればまとめて1ファイルにコンパイルしてしまえばrefarenceの記述をしなくても依存関係をちゃんと見てくれるみたい。まとめてコンパイルするときは以下の様なコマンドを使う

tsc –sorcemap -o output.js input1.js input2.js input3.js

オプションで指定してる[–sourcemap]はエラー時に圧縮前のtsファイルの行番号を出してくれるようになるらしい。つけといたほうが何かと便利。詳しいオプションは「tsc -h」で見れる

どう変換される?

module

moduleで外からも使用したい関数を使う場合はexportをつけてあげると参照できるようになる。

変換前:

module Page1 {
	function func1 {
		console.log('func1!');
	}
	export function func2 {
		console.log('func2!');
	}
}

変換後:

var Page1;
(function (Page1) {
    function func1() {
        console.log('func1!');
    }
    function func2() {
        console.log('func2!');
    }
    Page1.func2 = func2;
})(Page1 || (Page1 = {}));

class

publicとかprivateとか指定できるけど、何も意味ない。変換後は結局prototypeに登録される。prototypeに登録されたくない場合はstaticを使いましょう。

変換前:

class Page {
	private num : number = 3;
	public str : string = 'str';

	constructor() {
		this.num = 1;
		this.str = 'string'
	}

	private func1() {
		console.log('func1!');
	}

	public func2() {
		console.log('func2!');
	}

	static func3() {
		console.log('func3!');
	}
}

変換後:

var Page = (function () {
    function Page() {
        this.num = 3;
        this.str = 'str';
        this.num = 1;
        this.str = 'string';
    }
    Page.prototype.func1 = function () {
        console.log('func1!');
    };
    Page.prototype.func2 = function () {
        console.log('func2!');
    };
    Page.func3 = function func3() {
        console.log('func3!');
    }
    return Page;
})();

まとめ

とりあえずわかったのはこれぐらい。結構便利に使えそう。HashChangeでページをページの中身をAjaxでデータ取得して書き換えるとかやってるとどうしてもファイルが巨大になってしまうんだけど、こいつを使えば分割しておいて最終的にコンパイルして1ファイルにしてしまえばいいから保守性はかなり向上しそう。あとは継承とかをうまく使って書いてあげればコードは激減できそうだなぁ。

スマートフォンの画面で表示されている高さを取得する

UserAgentを取得して高さをUserAgent毎にごちゃごちゃやると上手くいく。

    // スマホ種別判定
    var ua = navigator.userAgent;
    if(ua.indexOf('Android') > 0) {
        var nPageH = document.height > document.width ? document.height : document.width;
        var nViewH = window.outerHeight > window.outerWidth ? window.outerHeight : window.outerWidth;
        if (nViewH > nPageH) {      
            nViewH = nViewH / window.devicePixelRatio;
            $('body').css('height', nViewH + 'px');
        }
    } else if(ua.indexOf('iPhone') > 0) {
        var nViewH = document.documentElement.clientHeight > document.documentElement.clientWidth ?
                                document.documentElement.clientHeight : document.documentElement.clientWidth;
        if (ua.indexOf('iPhone') && !window.navigator.standalone) nViewH += 60;
        $('body').css('height', nViewH + 'px');
    } else {
        return;
    }

これでやると結果的に高さがnViewHに入る。

history.back()で戻るとscrollイベントが取得できない

ハマったのでメモ。

挙動はタイトル通りなんだけど、iPhoneのSafariでhistory.back()を使用すると戻った画面でscrollイベントが取得できない模様。以下でscrollイベントを検出しようと3つを試してみたけどどれもだめ。

// jQueryで普通に
$(window).scroll(function() {
   console.log('scroll now');
});

// イベントにbindしてみる
$(window).on('scroll', function() {
    console.log('scroll now');
});

// 生のjavascriptで
window.onscroll = function() {
    console.log('scroll now');
};

history.back()で戻る前はどれもログが出力されるけど、戻った後は出力されない。Android標準ブラウザだと検出されるからiPhoneのSafariのバグなんじゃないかと疑っている。そもそもiPhoneのSafariとAndroid標準ブラウザだとscrollのイベント検出タイミングが

  • iPhoneのSafari:Scrollが完了したタイミングでイベント発生
  • Android標準ブラウザ:Scrollが発生している最中は定期的にイベントが発生し続ける

という風に違う。

PCブラウザだと、Android標準ブラウザと同じ挙動をするものが多い(*1)のでどうもiPhoneのSafariのScrollイベントとのbindがうまく動いてないんじゃないかと予想。position:fixed;のバグといい、iPhoneのSafariって出来が悪い気がする。ちなみにiOSの5.0で試してみました。なにか打開策をご存知のかたは教えてください。

追記

一応思いつく回避策を列挙。どれもぱっとしない。。。

  • history.back()を使わずに、location.hrefで頑張る
  • scrollイベントを使わずに、touchmoveイベントで頑張る
  • history.back()で戻ってきた場合は強制リロード

*1: Chrome, Firefoxで試した

JavaScriptの良い書き方とは

『JavaScript:The Good Parts 「良いパーツ」によるベストプラクティス』を読んだので覚書。内容はJavaScriptのバグの出にくい書き方をまとめた本。どちらかと言えば初心者向けの本で、こう書いたほうがいいですよーって感じのことを書いてる。けど、書き方が難しい。。。Good Partsの本と書いてあるけど、付録に記載のある「ひどいパーツ」「悪いパーツ」を見たほうが勉強になると思う。JavaScriptに特化していて、やらないほうがいいってことを書きだしてみる。

グローバル変数は使うな!

JavaScriptだとブロックスコープがないので、気をつけないとグローバル変数になってしまう。うまく関数スコープを使用してグローバル領域を汚さないようにプログラムを書きましょう。

(function(){
    // こんな感じで即時実行関数で括ってしまえば
    // グローバル領域を汚さない
    var hoge, hogehoge;
})()

==, != は使うな!

==, != の判定は予想外の行動を起こすことがある。==, != は、大抵の場合 ===, !== に置き換えれるため、こちらを使いましょう。

withは使うな!

JavaScriptではプログラムの記載を省略するためにwithを使用することができるが、こいつも予想外の行動を起こしやすいので使用しないようにしましょう。

newは使うな!

newを書き忘れるとバグの発見にすごく手間がかかるのでnewを使うのはやめましょう。って書いてあるんだけど、僕としてはnewは使うなというか、Classの概念を持ってくることはあまり好ましくないような気がしている。Classの概念とJavaScriptは相性が悪いと思う。

evalは使うな!

文字列をコンパイルして実行するevalを使うのは極力控えましょう。evalはコンパイルが走る分実行速度が遅くなります。

setTimeout、setIntervalに文字列を渡すな!

setTimeoutやsetIntervalでは第一引数に文字列を渡すとevalが走って実行することになります。そのため実行速度が遅くなります。わざわざevalを実行させる必要はないので、functionできっちりと定義しましょう。

まとめ

なかなか面白い本だった。記載してあるグッドプラクティスはすべての言語で共通して言えることも多いので、これからJavaScriptをはじめる人っていうよりもこれからプログラミングをはじめる人が読むといい本かもしれない。

JavaScriptの高速化試作の覚書 – JavaScriptグラフィックス

『JavaScriptグラフィックス – ゲーム・スマートフォン・ウェブで使う最新テクニック』ってのを読んだので必要そうな部分を抜粋

速度測定の指標

動作速度は端末によって違う。だからある一定の指標が必要。以下のコードを走らせることで、その端末で1秒間にどれだけの動作を行うことができるのかの指標を取得することができる。これを用いることでどの端末でも動作改善試作が表す影響を見ることができる。

var StartTime = new Date().getTime(); for(var count=0 ; timeElapsed < 1000 ; count++) { // ここで検証する動作を行う timeElaspsed = new Date().getTime() - startTime; } [/code]

countが増えれば増えるほど動作速度の向上を意味しており、改善前と改善後での数値の差に注目する。ただし、Firebugとかで計測できる環境があるならそれを使うのが一番いい。

高速化実装試作

  • 80-20のルール(20%のコードが80%のCPU時間を使う)に基づいて20%に力を入れて高速化をおこなったほうがいい
  • 三角関数等の重い計算処理を行うのであれば、結果をテーブルとして持ち近似値を使用したほうが早い
  • 複数の判定要素がある場合は、各ビットでフラグを表現し&演算子で判定を行ったほうが早い
  • XORをうまく使ってtoggleを実現できる(ex. toggle ^= 1;)
  • Math.Floor()よりもビットシフトを使用したほうが早い(ex. x = y >> 0)
  • jQueryのセレクタ指定は呼ばれるたびに検索が走るので複数呼ばない。複数呼ぶ場合は変数へキャッシュする。
  • jQueryのセレクタ指定は検索開始を指定することができる(ex. $(‘.class’, start-element);)
  • jQueryのcss演算子は遅い。複数変更する必要がある場合は、styleオブジェクトを抽出して変更する。(ex. $(‘#element’)[0].style)
  • 複数の要素を追加する場合は追加する文字列を作成して一回で追加したほうが早い。

アニメーション

どのような端末でも一定のスピードを保証するためにフレームレートを指定する。1秒間にどの程度の動作を行うことができるかを取得して、それに伴いアニメーションの移動距離を変えることでどの端末でも一意のスピードを保証することができる。例えば、1秒間に500回動作する端末(A)と1秒間に100回動さする端末(B)があった場合、どちらも同一のスピードを再現したいのであればBは1回の動作でAの移動距離の5倍移動させれば同じスピードを表現することができる。

まとめ

高速化について知りたくて呼んでみた本なんだけど、高速化の実装は最初の方にしか書いてなかったので非常に残念。アニメーションをJavaScriptで記載する場合(ゲーム等)は参考になる記述もたくさんあるようなので、もしゲームを作ることになったら更に深く読むことにする。