Oct 06, 2005

実践JavaScriptリファクタリング

同じ事をやるにも、いろんな書き方があるわけでいかにして短くてわかりやすいコードを書くかというノウハウを紹介します。
例として"abcde"を80回繰り返した文字列を作るとして実際に自分のコーディングスタイルがどんな風に変化していったのか、という。

短くなるのは確かなんだけどわかりやすいかというと、人によるかもしれない。

グローバル関数を定義

2年前なら、多分こういう具合だった。
//ふつうに関数として定義する
function x(str,num){
    var tmp = "";
    for(var i=0;i<num;i++){
        tmp += str;
    }
    return tmp;
}
x("abcde",80)

Stringのメソッドとして定義

1年前だとこんな感じ。
//Stringのメソッドとして定義する
String.prototype.x = function(num){
    var tmp = "";
    for(var i=0;i<num;i++){
        tmp += this
    }
    return tmp;
}
"abcde".x(80)

最近になって短くかけるところはなるべく短く書くようにしている。
//forの初期化ついでにtmpも宣言、{}を省略
String.prototype.x = function(num){
    for(var i=0,tmp="";i<num;i++) tmp += this;
    return tmp
}

こんな具合。見た目がすっきりする。

配列を使うようにする

ちょっとパフォーマンスが気になるので、高速化してみる。
文字列を加算していくのは、計算の途中で、
abcde、abcdeabcde、abcdeabcdeabcde
という文字列オブジェクトがその都度生成されていくのでメモリにやさしくない。
巨大な文字列の連結にはjoinを使ったほうが良い。数が大きくなると速度に差が出てくる。
//配列にpushしてjoin
String.prototype.x = function(num) {
    var tmp = [];
    for(var i=0;i<num;i++){
        tmp.push(this)
    }
    return tmp.join("")
}
//短く書くとこうなる
String.prototype.x = function(num){
    for(var i=0,tmp=[];i<num;tmp[i++]=this);
    return tmp.join("")
}

メソッドを使いまわす←いまここ

配列を埋めるArray#fillを作って、使いまわすようにする。
Array.prototype.fill = function(v){
    for(var i=0;i<this.length;this[i++]=v);
    return this
}
String.prototype.x = function(num){
    return Array(num).fill(this).join("")
}
"abcde".x(80)

これでString.prototype.xは、一行に収まった。
長さ80のArrayを"abcde"で埋めて連結する、と、意味そのままのコードになる。

もしも、あらゆるforループや一時変数を排除したいならば、次のように書くこともできる。
Array.prototype.fill = function(v){
    return this.map(function(){return v})
}
String.prototype.x = function(num){
    return Array(num).fill(this).join("")
}

まあ、どのみちArray.prototype.mapを自前で定義するならforループが必要ってことにはなるんだけど。
これは"abcde".x(10000)だったら1万回function(){return v}が呼ばれるので、動作が遅くなる。

※Array.prototype.mapはFirefox1.5でサポートされる新しいArrayのメソッドを参照。

まとめ

ビルトインオブジェクト拡張してしまうというのは、まあ行儀悪いと言えば行儀悪いんだけど、JavaScriptのprototypeベースとかオブジェクト指向とかの仕組みを理解するのには多分一番の近道なんじゃなかろうか。と思う。
Posted at 22:52 | WriteBacks (23) | Edit
Edit this entry...

wikieditish message: Ready to edit this entry.
















A quick preview will be rendered here when you click "Preview" button.