変数が配列かどうかを検出する方法


101

JavaScriptの変数が配列であるかどうかを判断するための、事実上の標準的なクロスブラウザーメソッドは何ですか?

ウェブを検索すると、いくつかの異なる提案があり、いくつかは良いものであり、かなりのものは無効です。

たとえば、以下は基本的なアプローチです。

function isArray(obj) {
    return (obj && obj.length);
}

ただし、配列が空の場合、またはobjが実際には配列ではなく長さプロパティを実装している場合などに注意してください。

では、実際に機能し、クロスブラウザでありながら効率的に機能するという点で、どの実装が最適ですか?


4
これは文字列に対してtrueを返しませんか?
James Hugard、

与えられた例は、質問自体に答えるメンターではなく、ソリューションへのアプローチ方法の単なる例です-特別な場合に失敗することがよくあります(この例のように、したがって、「しかし、注意してください...」)。
stpe、2009年

@ジェームズ:ほとんどのブラウザ(IEを除く)では、文字列は配列のようになっています(つまり、数値インデックスを介したアクセスが可能です)
Christoph

1
これを行うのが難しいとは信じられません...
Claudiu

回答:


160

JSのオブジェクトの型チェックを介して行われるinstanceof、すなわち

obj instanceof Array

各フレームには独自のArrayオブジェクトがあるため、オブジェクトがフレームの境界を越えて渡される場合、これは機能しません。この問題を回避するには、オブジェクトの内部[[Class]]プロパティを確認します。それを取得するには、Object.prototype.toString()(これはECMA-262で動作することが保証されています):

Object.prototype.toString.call(obj) === '[object Array]'

どちらの方法も実際の配列でのみ機能し、argumentsオブジェクトやノードリストのような配列のようなオブジェクトでは機能しません。すべての配列のようなオブジェクトには数値lengthプロパティが必要なので、次のようにチェックします。

typeof obj !== 'undefined' && obj !== null && typeof obj.length === 'number'

文字列はこのチェックに合格することに注意してください。IEでは文字列の文字へのインデックスによるアクセスが許可されていないため、問題が発生する可能性があります。したがって、に変更typeof obj !== 'undefined'typeof obj === 'object'て、プリミティブを除外し、タイプが'object'すべて異なるホストオブジェクトを除外することができます。これにより、文字列オブジェクトを渡すことができますが、手動で除外する必要があります。

ほとんどの場合、実際に知りたいのは、数値インデックスを介してオブジェクトを反復できるかどうかです。したがって、オブジェクトに0代わりにという名前のプロパティがあるかどうかを確認することをお勧めします。これは、次の確認のいずれかを介して実行できます。

typeof obj[0] !== 'undefined' // false negative for `obj[0] = undefined`
obj.hasOwnProperty('0') // exclude array-likes with inherited entries
'0' in Object(obj) // include array-likes with inherited entries

オブジェクトへのキャストは、配列のようなプリミティブ(つまり文字列)で正しく機能するために必要です。

JS配列の堅牢なチェックのコードは次のとおりです。

function isArray(obj) {
    return Object.prototype.toString.call(obj) === '[object Array]';
}

反復可能な(つまり、空でない)配列のようなオブジェクト:

function isNonEmptyArrayLike(obj) {
    try { // don't bother with `typeof` - just access `length` and `catch`
        return obj.length > 0 && '0' in Object(obj);
    }
    catch(e) {
        return false;
    }
}

7
js配列チェックの最先端技術のすばらしい要約。
Nosredna、2009年

MS JS 5.6(IE6?)以降、「instanceof」演算子をCOMオブジェクト(ActiveXObject)に対して実行すると、大量のメモリがリークしました。JS 5.7またはJS 5.8をチェックしていませんが、これはまだ当てはまる場合があります。
James Hugard、

1
@ジェームズ:興味深い-私はこのリークを知りませんでした。とにかく、簡単な修正があります。IEでは、ネイティブJSオブジェクトだけがhasOwnPropertyメソッドを持っているので、プレフィックスinstanceofとしてobj.hasOwnProperty && ;を付けます。また、これはまだIE7の問題ですか?タスクマネージャーを介した私の簡単なテストでは、ブラウザーを最小化した後にメモリが解放されることを示唆しています...
Christoph

@Christoph-IE7については不明ですが、IIRCはJS 5.7または5.8のバグ修正のリストに含まれていませんでした。基盤となるJSエンジンをサーバー側のサービスでホストするため、UIの最小化は適用されません。
James Hugard、

1
@TravisJ:ECMA-262 5.1、セクション15.2.4.2を参照してください。内部クラス名は慣例により大文字です- セクション8.6.2を
クリストフ

42

ECMAScript 5th Editionの登場により、変数が配列であるかどうかをテストする最も確実な方法であるArray.isArray()が得られます。

Array.isArray([]); // true

ここで受け入れられた答えは、ほとんどのブラウザーのフレームとウィンドウで機能しますが、別のウィンドウから配列で呼び出されるとが返されるため、Internet Explorer 7以下では機能しません。IE 9もこの動作に後退しているようです(以下の更新された修正を参照)。Object.prototype.toString[object Object][object Array]

すべてのブラウザーで機能するソリューションが必要な場合は、以下を使用できます。

(function () {
    var toString = Object.prototype.toString,
        strArray = Array.toString(),
        jscript  = /*@cc_on @_jscript_version @*/ +0;

    // jscript will be 0 for browsers other than IE
    if (!jscript) {
        Array.isArray = Array.isArray || function (obj) {
            return toString.call(obj) == "[object Array]";
        }
    }
    else {
        Array.isArray = function (obj) {
            return "constructor" in obj && String(obj.constructor) == strArray;
        }
    }
})();

それは完全に壊れないわけではありませんが、それを壊そうとしている誰かによって壊されるだけです。IE7以下とIE9の問題を回避します。 このバグはIE 10 PP2にも存在しますが、リリース前に修正される可能性があります。

PS、ソリューションについて不明な点がある場合は、心のこもったコンテンツでテストするか、ブログ投稿を読むことをお勧めします。条件付きコンパイルを使用することに不安がある場合は、他にも解決策が考えられます。


承認された回答はIE8 +では正常に機能しますが、IE6,7では機能しません
user123444555621

@ Pumbaa80:その通りです:-) IE8は独自のObject.prototype.toStringの問題を修正しますが、IE7以下のドキュメントモードに渡されるIE8ドキュメントモードで作成された配列をテストすると、問題は解決しません。私はこのシナリオのみをテストし、その逆は行いませんでした。この修正をIE7以下にのみ適用するように編集しました。
Andy E

WAAAAAAA、私はIEが嫌いです。さまざまな「ドキュメントモード」または「シゾモード」については忘れてしまいます。
user123444555621

アイデアは素晴らしく、見た目は良いですが、IE9のポップアップウィンドウではうまくいきません。それはオープナーによって作成された配列に対してfalseを返します... IE9互換ソリューションはありますか?(問題はとき、それはいけない、与えられた場合のために偽を返す、IE9のimplemnets自体をArray.isArrayことのようです。
ステファン・ハイル

@Steffen:興味深いので、のネイティブ実装はisArray他のドキュメントモードで作成された配列からtrueを返しませんか?私はいくつかの時間を取得するときに、このを検討する必要があるでしょうが、私はやるための最善のことは、ファイルは、それがIE 10に固定することができるので、Connectのバグだと思う
アンディ・E

8

Crockfordは、「良い部分」の106ページに2つの答えがあります。最初のものはコンストラクターをチェックしますが、異なるフレームまたはウィンドウにわたって偽陰性を与えます。これが2番目です。

if (my_value && typeof my_value === 'object' &&
        typeof my_value.length === 'number' &&
        !(my_value.propertyIsEnumerable('length')) {
    // my_value is truly an array!
}

Crockfordは、このargumentsメソッドは配列メソッドを持たない場合でも、配列を配列として識別すると指摘しています。

問題に関する彼の興味深い議論は105ページから始まります。

ここには、この提案を含む、さらに興味深いディスカッション(ポストグッドパーツ)あります。

var isArray = function (o) {
    return (o instanceof Array) ||
        (Object.prototype.toString.apply(o) === '[object Array]');
};

すべての議論で、何かが配列であるかどうかを知りたくありません。


1
これは、IEで文字列オブジェクトを破壊し、IEを除いて配列のような文字列プリミティブを除外します。実際の配列が必要な場合は、[[Class]]をチェックする方が適切です。配列のようなオブジェクトが必要な場合、チェックは制限が厳しすぎる
Christoph

@クリストフ-私は編集でもう少し追加しました。魅力的なトピック。
Nosredna、2009年

2

jQueryはisArray関数を実装しています。これは、これを行う最善の方法を示しています。

function isArray( obj ) {
    return toString.call(obj) === "[object Array]";
}

(jQuery v1.3.2から抜粋したスニペット-コンテキスト外で理解できるように少し調整されています)


IEでfalseを返します(#2968)。(jqueryソースから)
09年

1
jQueryソースのそのコメントは、isArray関数ではなくisFunction関数を参照しているようです
Mario Menger

4
使用する必要がありますObject.prototype.toString()-それは壊れる可能性が低いです
クリストフ

2

第一人者のJohn Resigとjqueryから盗む:

function isArray(array) {
    if ( toString.call(array) === "[object Array]") {
        return true;
    } else if ( typeof array.length === "number" ) {
        return true;
    }
    return false;
}

2
2番目のテストは、文字列に対してもtrueを返します:typeof "abc" .length === "number" // true
Daniel Vandersluis

2
さらに、「数値」のようなタイプ名をハードコードしてはいけません。代わりに、typeof(obj)== typeof(42)のような実際の数値と比較してみてください
ohnoes

5
@mtod:タイプ名をハードコーディングしてはいけないのはなぜですか?結局のところ、の戻り値typeofは標準化されていますか?
クリストフ

1
@ohnoesが説明します
Pacerier '

1

配列であると判断したら、値をどのように処理しますか?

あなたが含まれている値を列挙しようとする場合、それはたとえば、見え配列のようにするか、ハッシュテーブルとして使用されているオブジェクトの場合は、次のコードは、あなたが望むものを取得します(このコードは、閉鎖機能は、他の何かを返したときに停止します「未定義」よりも、COMコンテナまたは列挙を反復しないことに注意してください。これは読者のための演習として残されています)。

function iteratei( o, closure )
{
    if( o != null && o.hasOwnProperty )
    {
        for( var ix in seq )
        {
            var ret = closure.call( this, ix, o[ix] );
            if( undefined !== ret )
                return ret;
        }
    }
    return undefined;
}

(注:「o!= null」はnullと未定義の両方をテストします)

使用例:

// Find first element who's value equals "what" in an array
var b = iteratei( ["who", "what", "when" "where"],
    function( ix, v )
    {
        return v == "what" ? true : undefined;
    });

// Iterate over only this objects' properties, not the prototypes'
function iterateiOwnProperties( o, closure )
{
    return iteratei( o, function(ix,v)
    {
        if( o.hasOwnProperty(ix) )
        {
            return closure.call( this, ix, o[ix] );
        }
    })
}

答えは興味深いかもしれませんが、それは実際には質問とは何の関係もありません(そして、ところで、配列を介した反復for..inは悪いです[tm])
Christoph

@Christoph-もちろんです。何かが配列であるかどうかを判断する理由がいくつかあるに違いありません。値で何かを実行したいからです。(少なくとも私のコードでは)最も典型的なことは、配列内のデータをマップ、フィルター、検索、または変換することです。上記の関数はそれを正確に実行します。渡されたモノに反復可能な要素がある場合、それらはそれらに対して反復されます。そうでない場合は、何もせず、無害にundefinedを返します。
James Hugard、

@Christoph-for..in bad [tm]で配列を反復するのはなぜですか?他にどのように配列やハッシュテーブル(オブジェクト)を反復しますか?
James Hugard、

1
@James:for..inオブジェクトの列挙可能なプロパティを反復します。次の理由により、配列では使用しないでください。(1)遅いため。(2)順序を保持することは保証されていません。(3)ES3にはDontEnum属性を設定する方法が含まれていないため、オブジェクトまたはそのプロトタイプにユーザー定義プロパティセットが含まれます。私の心を滑らせた他の問題があるかもしれません
クリストフ

1
@Christoph-一方、for(;;)の使用は、スパース配列に対して適切に機能せず、オブジェクトプロパティを反復しません。#3は、あなたが言及した理由により、Objectには不適切なフォームと見なされます。一方、パフォーマンスに関しては、あなたの言うとおりです。1M要素の配列では、for..inはfor(;;)よりも36倍遅くなります。ワオ。残念ながら、オブジェクトプロパティ(ハッシュテーブル)を反復するという主な使用例には適用できません。
James Hugard、

1

CouchDB(SpiderMonkey)でこれを行う場合は、

Array.isArray(array)

としてarray.constructor === Arrayまたは機能array instanceof Arrayしません。を使用array.toString() === "[object Array]"しても機能しますが、比較するとかなり危険なようです。


:Node.jsの中とブラウザすぎだけでなく、CouchDBの中にこの作品developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
カール・ウィルバー


0

オンw3schoolかなり標準であるべき例があります。

変数が配列かどうかを確認するには、次のようなものを使用します

function arrayCheck(obj) { 
    return obj && (obj.constructor==Array);
}

Chrome、Firefox、Safari、IE7でテスト済み


constructor型チェックに使用するのは非常に脆弱です。代わりに提案された代替案の1つを使用してください
Christoph

何でそう思うの?もろい?
カマレイ2009年

@Kamarey:constructorプロトタイプオブジェクトの通常のDontEnumプロパティです。だれも愚かなことをしない限り、これは組み込み型の問題ではないかもしれませんが、ユーザー定義型の場合は簡単に問題があります。私のアドバイス:常にを使用しますinstanceof。これは、プロトタイプチェーンをチェックし、任意に上書きできるプロパティに依存しません
Christoph

1
おかげで、ここでは別の説明が見つかりました:thinkweb2.com/projects/prototype/...
Kamarey

Arrayオブジェクト自体がカスタムオブジェクトで上書きされる可能性があるため、これは信頼できません。
Josh Stodola、

-2

この関数の最もよく研​​究され議論されたバージョンの1つは、PHPJSサイトにあります。パッケージにリンクすることも、関数に直接移動することもできます。私は、JavaScriptでPHP関数と同等に構築された同等のサイトを強くお勧めします。


-2

コンストラクタと同等の参照が不十分です。時々彼らはコンストラクタの異なる参照を持っています。だから私はそれらの文字列表現を使用します。

function isArray(o) {
    return o.constructor.toString() === [].constructor.toString();
}

だまされる{constructor:{toString:function(){ return "function Array() { [native code] }"; }}}
ベルギ

-4

交換するArray.isArray(obj)ことにより、obj.constructor==Array

サンプル:

Array('44','55').constructor==Array trueを返す(IE8 / Chrome)

'55'.constructor==Array falseを返す(IE8 / Chrome)


3
正しい機能を恐ろしいものに置き換えるのはなぜですか?
Bergi、2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.