2005年10月28日
川俣晶の縁側ソフトウェア技術雑記total 18119 count

これは凶悪だっっっ! JavaScriptの"for~in"構文の罠

Written By: 川俣 晶連絡先

 JavaScriptには、コレクションを列挙する"for~in"構文があります。

 これはC#のforeachや、Visual BasicのFor Eachに相当する素敵でラクチンな機能か……と思いきや。検索してみると、使用している事例を全く見かけません。

 その理由(?)らしきものを調べたので、まとめておきます。

まずはC#でforeachを試す §

 C#やVisual Basicのプログラマにとって、直感的で良く分かるソースから。

using System;

using System.Xml;

class Class1

{

    [STAThread]

    static void Main(string[] args)

    {

        string srcDoc = "<html><p>line1</p><p>line2</p><p>line3</p></html>";

        XmlDocument doc = new XmlDocument();

        doc.LoadXml( srcDoc );

        XmlNodeList pCollection = doc.GetElementsByTagName("p");

        foreach( XmlNode node in pCollection )

        {

            Console.WriteLine("[{0}] ({1})",node.ToString(),node.InnerText);

        }

    }

}

 実行結果は以下の通り。

[System.Xml.XmlElement] (line1)

[System.Xml.XmlElement] (line2)

[System.Xml.XmlElement] (line3)

 コレクションの個々のアイテムが、ループ変数nodeに入っています。

Internet Explorer 6.0で試す §

 HTML DOMからp要素を抜き出して列挙するコードを書いてみました。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<title>test</title>

<script><!--

function onLoad()

{

    var result = "";

    var pCollection = document.getElementsByTagName('p');

    for( var index in pCollection )

    {

        result += "<p>[" + index + "]=[" + pCollection[index] + "]</p>";

    }

    document.getElementById('result').innerHTML = result;

}

// --></script>

</head>

<body onload="onLoad()">

<p>line1</p>

<p>line2</p>

<p>line3</p>

<div  id="result">

</div>

</body>

</html>

 実行結果は以下の通り。なお、最初の3行は列挙する対象。残りが、列挙した結果です。

line1

line2

line3

[length]=[3]

[0]=[[object]]

[1]=[[object]]

[2]=[[object]]

 というわけで、C#やVisual Basicとは2つの決定的な相違が見えてきました。

 第1に、ループ変数に入ってくる値は、コレクションの値ではなく、キーであること。つまり、コレクションの値を取り出すには、キーを使ってあらためて取り出す操作を記述しなければなりません。(つまり、indexではなくpCollection[index]と書かねばならないということ)。

 第2に、オブジェクトが持っている(おそらく)全ての情報が列挙されていること。つまり、コレクションの長さを示すlengthの値も列挙された結果、3個のオブジェクトを含むコレクションを列挙させたにもかかわらず、ループは4回繰り返されます。

Firefox 1.0.7で試す §

 同じコードをFirefox 1.0.7で実行すると以下の通り。

line1

line2

line3

[0]=[[object HTMLParagraphElement]]

[1]=[[object HTMLParagraphElement]]

[2]=[[object HTMLParagraphElement]]

[length]=[3]

[item]=[ function item() { [native code] } ]

[namedItem]=[ function namedItem() { [native code] } ]

 なんと、列挙される回数が2回増えています。

Opera 8.5で試す §

 Opera 8.5で試してみると、更にびっくり。

line1

line2

line3

[0]=[[object HTMLParagraphElement]]

[1]=[[object HTMLParagraphElement]]

[2]=[[object HTMLParagraphElement]]

[length]=[3]

[item]=[ function item() { [native code] }]

[namedItem]=[ function namedItem() { [native code] }]

[tags]=[ function tags() { [native code] }]

 なんとなんと、列挙される回数がFirefoxより1回増えています。つまり、IEと比較すると3回増えています。

結論 §

 JavaScriptの"for~in"構文は、C#やVisual Basicのforeach(For Each)構文とはムードが似ているだけで機能性が全く違います。つまり、同じように使えることを期待すべきではないのでしょう。

 また、Webブラウザ間の非互換性が露呈しやすい機能であることも確認できました。様々なオブジェクトは、個々の環境に固有の情報が追加される可能性があり得ますが、"for~in"構文の利用はその相違に対して敏感に反応してしまいます。

 というわけで、"for~in"構文は使わない方が安全かつ確実、ということのようです。利用例がほとんど見付からないのも当然ですね。

メモ §

 ECMA-262に書かれている"DontEnum 属性"は、この件に関係があるのだろうか?

Facebook

トラックバック一覧

2008年06月27日[JavaScript]はじめてのLog4jsFrom: babydaemonsの日記

社内環境で再現しない不具合が残ってしまったため、ログを取得して障害再発時に解析のネタを得ようとしています。怪しいのはクライアントサイドのJavaScriptです。車輪の再発明を避けるべく、ありがたく先人達の成果を使わせていただきます。 Log4jsを入手する - Object汚染 続きを読む

2007年06月29日mathesの挙動From: enchanting an air of joyous bliss

JavaScriptの文字列には指定した正規表現で文字列のマッチングを行うmatchメソッドが用意されている。 例えば時刻のフォーマットのチェックを行うには次のようにすることができる //"true"が表示される alert( "12:23:43".match(/[12]+\d:[0-5]\d:[0-5]\d/)) また、オプション"g"を指定することで、最初にマッチした部分に加え、2番目、3番目...にマッチした部分も配列として返すこともできる。 //12時23分43秒と表示される var collection = "12:23:43".match(/\d+/g); alert(collection[0]+"時"+collection[1]+"分"+collection[2]+"秒"); また、上の例ではfor-in構文を使って配列の中身を列挙できる//画面に12,23,43と表示される・・・はず? for(var i=0 in collection){ alert(collection[i]) } はずなのだが、FireFoxでは12,23,43と期待した通りに表示されるのだがIEだと変な値まで表示されるようになる。 ちなみに、次の構文だとIEでも12,23,43表示される。//これはIEでも画面に12,23,43と表示される for(var i=0 i < collection.length;i++){ alert(collection[i]) }どうやら、IEではmatchで返却される配列中身を走査するときfor-in構文とfor(;;)構文とでは挙動が変わってしまうらしい。 *googleで"javascript for in"で検索したら、次のサイトが見つかった "これは凶悪だっっっ! JavaScriptの"for〜in"構文の罠" IEだけでなく、他のブラウザもどうやらfor-inの構文の挙動はC#やjavaのものとは違った動作をするようですね・・・・。 FireFoxも昔の1.07のバージョンでは変な動きをするようですが・・・・ ちなみに、今私の使っているFireFox1.5はC#やJavaと同じ 続きを読む

このコンテンツを書いた川俣 晶へメッセージを送る

[メッセージ送信フォームを利用する]

メッセージ送信フォームを利用することで、川俣 晶に対してメッセージを送ることができます。

この機能は、100%確実に川俣 晶へメッセージを伝達するものではなく、また、確実に川俣 晶よりの返事を得られるものではないことにご注意ください。

このコンテンツへトラックバックするためのURL

https://mag.autumn.org/tb.aspx/20051028175955
サイトの表紙【技術雑記】の表紙【技術雑記】のコンテンツ全リスト 【技術雑記】の入手全リスト 【技術雑記】のRSS1.0形式の情報このサイトの全キーワードリスト 印刷用ページ

管理者: 川俣 晶連絡先

Powered by MagSite2 Version 0.36 (Alpha-Test) Copyright (c) 2004-2021 Pie Dey.Co.,Ltd.