WebKitでサポートされているJavaScript1.6のべんりなArrayメソッド

常識なのかもしれないですけど、先日たまたまJavaScript1.6で追加されたArrayの拡張メソッドがWebKitでもサポートされているのに気がつきました。

ちゃんとした文書をみつけられなかったのですが[webkit-reviews] review denied: [Bug 6252] JavaScript 1.6 Array.lastIndexOf : [Attachment 10095] lastIndexOf javascript patchというのが出てきたので、WebKitでも正式にJavaScript1.6がサポートされてるようです。

こういうときにべんり

追加されたメソッドで最も便利なのがmap()です。mapは引数に関数を渡して使います。配列の各要素を引数にして渡された関数を実行して、関数の返した値を配列として返してくれます。mapは戻り値として配列を返すのに対して、何も返さないforEachもあります。何も返さないぶんforEachのほうが高速なんだと思いますが、タイプするときに難しいので、値を返さないときでも私はもっぱらmapを使っています。 mapの何が便利なのか、例としてjavascriptでURLのクエリ文字列をパースして、ひとつのオブジェクトにパラメータの名前をキーにして値を保持する処理を考えてみましょう。mapを使わずにふつうにforを使って書くとこうです。
var params = {};
var q = "q=prototype.js&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:official&client=firefox-a";
var pairs = q.split(/&/);
for(var i = 0; i < pairs.length; i++ ) {
    var pair = pairs[i];
    var nv = pair.split(/=/);
    var n = decodeURIComponent( nv[0] );
    var v = decodeURIComponent( nv[1] );
    params[n] = v;    
}
ループをまわすためにforを書いたり、URLエンコードされているかもしれない名前と値をデコードするために一度変数にいれたりしないといけません(params[ decodeURIComponent(nv[0]) ] = decodeURIComponent(nv[1])って書くのはちょっと嫌ですよね)。


おなじ処理をmapを使って書くとこんなにすっきりします。

var params = {};
var q = "q=prototype.js&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:official&client=firefox-a";
q.split( /&/ ).map( function (pair) {
    var nv = pair.split(/=/).map( decodeURIComponent );
    params[ nv[0] ] = nv[1];
} );

mapの引数にした関数に配列の要素の値が直接渡されるので、インデックスのための変数が必要なくなるのと、パラメータの名前と値をデコードするのに2回decodeURIComponentを書かなくてよくなるのですっきりします。


今回はわりとなじみがありそうな例としてクエリ文字列のパースをあげました。関数を引数に渡すのはなんか難しそうですが、forでループで書くのにくらべてmapだと書かないといけない量が少ないのでとりあえず楽ができます。


ただけっきょくIEでサポートされてるわけではないので、SafariでJavaScript1.6のArray拡張が実装されていてもiPhone/iPod touch用のサイトくらいしか使える機会がないのが悲しいところです...

thisに注意!

ただクラスの中でmapを使うときには注意です。引数に渡した関数の中ではthisの値が変わっています(windowオブジェクトになっています)。さっきのコードがもし下のようにクラスメンバのparamsに結果を代入するようになっているとParser.paramsではなくwindow.paramsに結果が入ってしまいます。
var Parser = {
    params: {},
    parse: function (q) {
        q.split( /&/ ).map( function (pair) {
        var nv = pair.split(/=/).map( decodeURIComponent );
            this.params[ nv[0] ] = nv[1];
        } );
    }
}

これを避けるには2つ方法があります。

よく使われているのが、mapの前にthisをselfに代入しておく方法です。一時的に代入しておくための変数はselfでなくても何でもいいですが、慣習としてselfが使われます。
var Parser = {
    params: {},
    parse: function (q) {
        var self = this;
        q.split( /&/ ).map( function (pair) {
            var nv = pair.split(/=/).map( decodeURIComponent );
            self.params[ nv[0] ] = nv[1];
        } );
    }
}
もうひとつはmapの第2引数にthisになるオブジェクトを指定する方法です(さっき知りました...)。関数内のthisで示されるオブジェクトを第2引数で変更することができます。
var Parser = {
    params: {},
    parse: function (q) {
        q.split( /&/ ).map( function (pair) {
            var nv = pair.split(/=/).map( decodeURIComponent );
            this.params[ nv[0] ] = nv[1];
        }, this );
    }
}
前者はスコープチェインをたどってselfという名前が解決されるのにくらべて、後者の方は同じスコープに名前があるぶん処理が軽いでしょう。WebKitでも同様に第2引数で関数内のthisを変更することができます。

JavaScript1.8だとさらに短く書けます!

WebKitでサポートされているのはJavaScript1.6のでですが、FirefoxのJavaScript1.8ではさらなるArrayの拡張というのがサポートされています。

上のクエリ文字列のパースは、さらにJavaScript1.7で導入された分割代入とさらなるArrayの拡張に含まれているreduceを使うとさらにすっきりさせることができます。


var q = "q=prototype.js&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:official&client=firefox-a";
var params = q.split( /&/ ).reduce( function (_params, pair) {
    var [n, v] = pair.split(/=/).map( decodeURIComponent );
    _params[ n ] = v;
    return _params;
}, {} );

というふつうに便利なのでIEでも使えるようになるといいですね。

tags

  • WebKit
  • javascript
  • 「WebKitでサポートされているJavaScript1.6のべんりなArrayメソッド」のはてなブックマーク数
  • 「WebKitでサポートされているJavaScript1.6のべんりなArrayメソッド」deliciousブックマーク数
  • 「WebKitでサポートされているJavaScript1.6のべんりなArrayメソッド」をはてなブックマークに追加
  • save "WebKitでサポートされているJavaScript1.6のべんりなArrayメソッド" to del.icio.us
  • 「WebKitでサポートされているJavaScript1.6のべんりなArrayメソッド」をリアルタイムブログ検索
  • permalink
  • javascriptで作るGearmanクライアント
  • サービスのread/writeとコンテンツのfav

comments

TypeKey Enabled
スタイル用のHTMLタグが使えます。
*required

trackbacks

トラックバック元エントリにこのエントリへのリンクがない場合はトラックバックを受け付けません。

http://labs.gmo.jp/mt/mt-tb.cgi/220
©2010 Kentaro Kumagai, GMO Internet Labs., GMO Internet, inc.
bits and bytes
2008 .06. 13 22:41

tagcloud

  • API2
  • C/C++2
  • E4X1
  • FUSE2
  • Firefox28
  • HTML4
  • IE1
  • MySQL1
  • OSX4
  • Opera2
  • PHP4
  • UI2
  • WebKit1
  • XML1
  • XPCOM4
  • XPath4
  • apache2
  • binary2
  • book1
  • data13
  • debug5
  • design1
  • experiments4
  • extension14
  • geo1
  • google gears2
  • google maps API1
  • greasemonkey4
  • httpd5
  • javascript22
  • linux1
  • logging2
  • mobile4
  • perl5
  • tips6
  • tool11
  • vim2
  • visualization3
  • widget1
  • wii3
  • windows7
  • サービス7
  • 和訳1

Archives

  • 2008.06 (3)
  • 2008.05 (5)
  • 2008.04 (4)
  • 2008.03 (4)
  • 2008.02 (6)
  • 2008.01 (3)
  • 2007.12 (4)
  • 2007.11 (5)
  • 2007.10 (4)
  • 2007.09 (4)
  • 2007.08 (4)
  • 2007.07 (8)
  • 2007.06 (7)
  • 2007.05 (4)
  • 2007.04 (5)
  • 2007.03 (6)
  • 2007.02 (4)
  • 2007.01 (6)

about

  • bits and bytesのXML
  • 「bits and bytes」のCreative Commons
  • Powered by Movable Type
  • イベントと地図 - モグ
  • Use ecto to blog!
  • bits and bytesのはてなブックマーク数
  • bits and bytesをMy Yahoo!に追加
  • Subscribe with Bloglines