ループに関する覚書

ループと言えばスクリプトになくてはならない処理体系ですよね。
JSが苦手と思ってたときは配列やらループやらを全然理解できてなかったことが一因でしたが、
それを自分の中で消化できたら途端に面白くなってきました。
それで、ループ(配列を処理する場合)と一言で言っても色々な書き方があるのだなあとわかったの覚書として残しておきます。
あ、ちなみにforループとjQueryのeachしか使ったことがありません!!(キリッ

いい例が思い浮かびませんでしたが、例えばこんなHTMLがあるとして、各li要素のテキストを抜き出すスクリプトを書くとします。

<div id="loop">
    <section>
        <h1>ループ</h1>
        <ul>
            <li>ボタン1</li>
            <li>ボタン2</li>
            <li>ボタン3</li>
            <li>ボタン4</li>
            <li>ボタン5</li>
        </ul>
    </section>
</div>

今までの僕がJSで配列をループで回すとしたらこの書き方しかしりませんでしたあ!!!
jQueryを使うケースも書いているので、配列はjQueryで作っています。

ネイティブJSのオーソドックスな書き方

var array = $('li', '#loop');
for (var i=0,n=array.length;i<n;i++) {
    console.log(array[i].innerHTML, 'ループその1');
}

これは特に説明不要の超オーソドックスバージョンです。
配列の数を変数に入れてキャッシュ(?)させたほうがちょっと速いと聞いたのでそうしてます。処理構造を考えると理解ができる気はしますね。
変数arrayに格納しているliの選択のしかたですが、#loop liにするよりも上記の書き方のほうがコストが少ないとみたのでこう書いています。
下記のコードのほうがさらにコストが少ないと書いてあった気はするのですが、なんとなく無難な書き方(?)でやっています。

var array = $('ul', '#loop').find('li');

jQueryのループ処理

で、次はjQueryを使ったバージョンです。

array.each(function() {
	console.log(this.innerHTML, 'ループその4');
});

こちらもはjQueryのオーソドックスなループかと。
配列に対してeachメソッドを実行して、コールバック関数に処理を書きます。
引数を設定することができ、第一引数でインデックス番号、第二引数で値を渡せます。
ちなみにこの書き方だと配列しか処理できませんが、次の書き方だとオブジェクトもループで回せます。

$.each(array, function() {
	console.log(this.innerHTML, 'ループその5');
});

第一引数に処理する配列もしくはオブジェクトを渡して、第二引数でコールバック関数を渡します。jQuery版の1個目と同じくインデックス番号と値を引数で渡せます。オブジェクトを処理する場合はキーと値を引数で渡せます。

jQueryクックブックに教えてもらったネイティブJSの高速な(?)書き方

以上はまあ標準的な配列を処理できるループなのですが、以下はjQueryクックブックを読んでて知った書き方です。特殊なケースでしか使えないので極力オーソドックスなものを利用してこれは多用はしないほうが良さそうですが、知っておいて損はなさそう。

1つ目が

for (var item,i = -1; item = array[++i];) {
	console.log(item.innerHTML, 'ループその2');
}

uupaaさんがオーソドックスループでもi++ではなく++iを使うべきと推奨していましたが、処理のタイミングが違い、一部のブラウザでは++iの方が高速なようです。(ただ、最近テストしてらっしゃって最新のブラウザでは必ずしも++iの方が速いわけではないという結果が出たみたいですが…)
iを-1で初期化することによりitemに代入するタイミングで[++i]の数字が0になるようです。
この書き方はarray[++i]の値がfalseになるまで繰り返されます。配列の処理の途中でfalseが返ってくると、全ての処理が終わる前にループが終了してしまうので、気軽に使わないほうがいいと思います。

最後はオーソドックスなループの無駄を無くす方法です。

for (var i=-1,n=array.length;++i<n;) {
    console.log(array[i].innerHTML, 'ループその3');
}

1つ目から改良された点は

  • 一部のブラウザでi++より高速な++iを使う
  • ループ変数の評価とインクリメントを組み合わせて名前参照をひとつ減らす

まあ、色々方法を書き連ねましたが、自分で速さを計測したわけではないので現時点でどの書き方が一番速いのかわかりません!!
以上、無責任な記事でした!!!!

「ループに関する覚書」への5件のフィードバック

  1. 知らない事だらけですね。「++i」とはいったい・・・。
    for()の評価は「i>n」とかじゃなくてもfalseが返ってくるようになっていたらよかったんですね。
    わかっていたようでわかっていなかったような・・・。
    for(i=0;i>n;i++)みたいな書き方しか知らなかったので勉強になりますw

  2. 僕も完全に理解しているわけではないのですが、「i++」と「++i」では加算のタイミングが違うようですね。
    ループで処理する内容にfalseを返す何かが含まれていない場合は最後の
    for (var i=-1,n=array.length;++i<n;) {
    console.log(array[i].innerHTML, 'ループその3');
    }
    が最速らしく、jQueryオブジェクトをループで回す場合は$.each()とかではなくこれを使うのが良いと書かれていました。

    まあ、ちゃんと理解しきっていないのでかなり危うい情報ですが…。
    ちなみに、オーソドックスなループの書き方でi++を++iと書くことで、IE6、7、8の処理速度が多少改善されるようです。
    あとちょっとしたら消えてしまうテクニックですけどね~…

  3. やばいっすなー。$.each()は内部でガチャガチャやってるんでしょうね。。
    今度から意識しておこう!とは思いますが、とりあえず$.each()つかってその後にチューンアップする感じをまずはとっていきますかね・・・。
    高速化に目を向けていくといいスクリプトが書けるようにだんだん洗練されてくかもしれないすね。

  4. 僕は順番関係ないならこれ使ってる。
    var len = arr.length;
    while(len–){
    console.log(len);
    }
    0はfalseということみたいです。

  5. > shinsakuさん
    色々な書き方がありますね。
    スクリプト歴が短いのもあってwhileループを使ったことがありません。。
    この書き方がすっきりしてて一番わかりやすいかもしれませんね。

    ちょうど昨日、本を読んでて条件式の結果が0、null、undefined、’’の場合はfalseに変換されると知りました…!

コメントは停止中です。