변수가 배열인지 감지하는 방법


101

JavaScript의 변수가 배열인지 여부를 결정하는 데 가장 좋은 사실상의 표준 크로스 브라우저 방법은 무엇입니까?

웹 검색에는 여러 가지 제안이 있으며, 일부는 훌륭하고 일부는 유효하지 않습니다.

예를 들어 다음은 기본적인 접근 방식입니다.

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

그러나 배열이 비어 있거나 obj가 실제로 배열이 아니지만 길이 속성 등을 구현하면 어떤 일이 발생하는지 확인하십시오.

그렇다면 실제로 작동하고 브라우저를 넘나들며 효율적으로 수행하는 측면에서 가장 좋은 구현은 무엇입니까?


4
이것은 문자열에서 true를 반환하지 않습니까?
James Hugard

주어진 예는 질문 자체에 대한 답이 아니라 솔루션에 접근 할 수있는 방법의 예일뿐입니다. 특수한 경우에 종종 실패합니다 (예 : "그러나, 참고 ...").
stpe 2009-06-29

@James : 대부분의 브라우저 (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'alltogether을. 이것은 여전히 ​​문자열 객체를 통과시킬 수 있으며, 수동으로 제외해야합니다.

대부분의 경우 실제로 알고 싶은 것은 숫자 인덱스를 통해 객체를 반복 할 수 있는지 여부입니다. 따라서 객체에 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-06-29

MS JS 5.6 (IE6?)에서 "instanceof"연산자는 COM 개체 (ActiveXObject)에 대해 실행될 때 많은 메모리를 유출했습니다. JS 5.7 또는 JS 5.8을 확인하지 않았지만 여전히 사실 일 수 있습니다.
James Hugard

1
@James : 흥미 롭습니다-나는이 누출에 대해 몰랐습니다; 어쨌든 간단한 수정이 있습니다. IE에서는 네이티브 JS 개체에만 hasOwnProperty메서드가 있으므로 접두사 instanceofobj.hasOwnProperty && ; 또한 이것은 IE7에서 여전히 문제입니까? 작업 관리자를 통한 내 간단한 테스트에 따르면 브라우저를 최소화 한 후 메모리가 회수 된 것으로 나타났습니다.
Christoph

@Christoph-IE7에 대해서는 확실하지 않지만 IIRC는 JS 5.7 또는 5.8의 버그 수정 목록에 없습니다. 서비스의 서버 측에서 기본 JS 엔진을 호스팅하므로 UI를 최소화 할 수 없습니다.
James Hugard 2009-06-29

1
@TravisJ : ECMA-262 5.1, 섹션 15.2.4.2 참조 ; 내부 클래스 이름은 관례 상 대문자입니다. 섹션 8.6.2
Christoph

42

ECMAScript 5th Edition이 출시되면서 변수가 배열 인 경우 가장 확실한 테스트 방법 인 Array.isArray ()가 제공됩니다 .

Array.isArray([]); // true

여기에 허용 대답은 대부분의 브라우저에 대한 프레임과 창문을 통해 작동하지만, 그것은 낮은 인터넷 익스플로러 7이 켜지지 않고 있기 때문에, Object.prototype.toString반환하는 다른 창에서라는 배열에 [object Object]하지 [object Array]. IE 9도이 동작으로 회귀 한 것으로 보입니다 (아래 업데이트 된 수정 사항 참조).

모든 브라우저에서 작동하는 솔루션을 원한다면 다음을 사용할 수 있습니다.

(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에 여전히 존재 하지만 출시 전에 수정 될 수 있습니다.

추신, 솔루션에 대해 확실하지 않은 경우 마음에 드는 내용을 테스트하거나 블로그 게시물을 읽는 것이 좋습니다. 조건부 컴파일을 사용하는 것이 불편하다면 다른 잠재적 인 해결책이 있습니다.


수락 된 답변 은 IE8 +에서는 잘 작동하지만 IE6,7에서는 작동하지 않습니다
user123444555621

@ Pumbaa80 : 맞습니다 :-) IE8은 자체 Object.prototype.toString에 대한 문제를 해결하지만 IE7 이하 문서 모드로 전달되는 IE8 문서 모드에서 생성 된 배열을 테스트하면 문제가 지속됩니다. 나는이 시나리오만을 테스트했고 그 반대는 아니었다. 이 수정 사항을 IE7 이하에만 적용하도록 편집했습니다.
Andy E

WAAAAAAA, 나는 IE가 싫어. 다른 "문서 모드"또는 "정신 모드"를 완전히 잊어 버렸습니다.
user123444555621

아이디어가 훌륭하고보기에도 좋지만 팝업 창이있는 IE9에서는 작동하지 않습니다. 오프너에 의해 생성 된 배열에 대해 false를 반환합니다. IE9 호환 솔루션이 있습니까? . (문제는 IE9의 implemnets는 말아야 때 주어진 경우에 false를 반환하는 자체 Array.isArray 것 같다
스테 하일

@Steffen : 흥미 롭습니다.의 기본 구현이 isArray다른 문서 모드에서 생성 된 배열에서 true를 반환하지 않습니까? 시간이되면이 문제를 조사해야하지만 가장 좋은 방법은 Connect에 버그를 신고하여 IE 10에서 수정할 수 있도록하는 것입니다.
Andy E

8

Crockford는 "The Good Parts"의 106 페이지에 두 가지 답변이 있습니다. 첫 번째는 생성자를 확인하지만 다른 프레임이나 창에서 거짓 부정을 제공합니다. 두 번째는 다음과 같습니다.

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

@ Christoph-- 편집을 통해 조금 더 추가했습니다. 매혹적인 주제.
Nosredna 2009-06-29

2

jQuery는 isArray 함수를 구현하는데,이를 수행하는 가장 좋은 방법은 다음과 같습니다.

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

(jQuery v1.3.2에서 가져온 스 니펫-문맥에 맞도록 약간 조정 됨)


IE에서 false를 반환합니다 (# 2968). (jquery 소스에서)
2009 년

1
JQuery와 소스의 그 코멘트는하지 끝나면 IsArray isFunction 기능을 참조 할 것
마리오 멩거

4
당신은 사용해야합니다 Object.prototype.toString()-그것은 깨질 가능성이 적습니다
Christoph

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
두 번째 테스트는 문자열에 대해서도 true를 반환합니다. typeof "abc".length === "number"// true
Daniel Vandersluis

2
또한 "숫자"와 같은 유형 이름을 하드 코딩해서는 안됩니다. 대신 실제 숫자와 비교해보십시오. typeof (obj) == typeof (42)
ohnoes

5
@mtod : 유형 이름을 하드 코딩하면 안되는 이유는 무엇입니까? 결국의 반환 값 typeof이 표준화됩니까?
Christoph

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 및 undefined 모두에 대해 테스트)

사용 예 :

// 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] );
        }
    })
}

(를 통해 배열을 반복하고 BTW 대답은 흥미로운 일이 될 수도 있지만, 정말 문제와 아무 상관이없는 for..in나쁜 [TM]입니다)
크리스토프

@Christoph-물론입니다. 무언가가 배열인지 결정하는 데에는 몇 가지 이유가 있어야합니다. 값으로 무언가를하고 싶기 때문입니다. 가장 일반적인 것은 (적어도 내 코드에서) 배열의 데이터를 매핑, 필터링, 검색 또는 변환하는 것입니다. 위의 함수는 정확히이를 수행합니다. 전달 된 항목에 반복 할 수있는 요소가있는 경우 해당 요소를 반복합니다. 그렇지 않은 경우 아무 작업도 수행하지 않고 정의되지 않은 결과를 무해하게 반환합니다.
James Hugard

@Christoph-for..in bad [tm]로 배열을 반복하는 이유는 무엇입니까? 배열 및 / 또는 해시 테이블 (객체)을 어떻게 반복 하시겠습니까?
James Hugard

1
@James : for..in개체의 열거 가능한 속성을 반복합니다. 다음과 같은 이유로 배열과 함께 사용해서는 안됩니다. (1) 느립니다. (2) 질서를 유지하는 것이 보장되지 않습니다. (3) ES3는 DontEnum 속성을 설정하는 방법을 포함하지 않기 때문에 객체 또는 프로토 타입에 설정된 사용자 정의 속성을 포함합니다. 내 마음을 미끄러 뜨린 다른 문제가있을 수 있습니다
Christoph

1
@Christoph-반면에 for (;;) 사용은 희소 배열에 대해 제대로 작동하지 않으며 개체 속성을 반복하지 않습니다. # 3은 언급 한 이유 때문에 Object에 대해 잘못된 형식으로 간주됩니다. 반면에, 성능면에서 당신은 옳습니다. for..in은 1M 요소 배열에서 for (;;)보다 ~ 36 배 느립니다. 와. 불행히도 객체 속성 (해시 테이블)을 반복하는 주요 사용 사례에는 적용되지 않습니다.
James Hugard 2009-06-29



0

w3school 매우 표준해야한다 예를 들어이있다.

변수가 배열인지 확인하기 위해 다음과 비슷한 것을 사용합니다.

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

Chrome, Firefox, Safari, ie7에서 테스트 됨


constructor유형 검사에 사용 하는 것은 너무 부서지기 쉽습니다. 대신 제안 된 대안 중 하나를 사용하십시오
Christoph

왜 그렇게 생각하세요? 취성에 대해?
Kamarey 2009-06-29

@Kamarey : constructor프로토 타입 객체의 일반 DontEnum 속성입니다. 아무도 어리석은 짓을하지 않는 한 내장형에 대해서는 문제가되지 않을 수도 있지만, 사용자 정의 유형의 경우에는 쉽게 할 수 있습니다. 내 조언 : 항상 사용 instanceof, 프로토 타입 체인을 확인하고 임의로 덮어 쓸 수있는 속성에 의존하지 않습니다
Christoph

1
감사합니다. 여기에서 다른 설명을 찾았습니다. thinkweb2.com/projects/prototype/…
Kamarey 2009-06-29

Array 개체 자체를 사용자 지정 개체로 덮어 쓸 수 있기 때문에 신뢰할 수 없습니다.
Josh Stodola


-2

생성자와 동일한 참조가 충분하지 않습니다. 때때로 그들은 생성자에 대한 다른 참조를 가지고 있습니다. 그래서 저는 그것들의 문자열 표현을 사용합니다.

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

속지{constructor:{toString:function(){ return "function Array() { [native code] }"; }}}
BERGI

-4

교체 Array.isArray(obj)obj.constructor==Array

샘플 :

Array('44','55').constructor==Array true 반환 (IE8 / Chrome)

'55'.constructor==Array false 반환 (IE8 / Chrome)


3
왜 기능을 끔찍한 것으로 바꾸겠습니까?
Bergi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.