document.querySelectorAll이 실제 배열이 아닌 StaticNodeList를 반환하는 이유는 무엇입니까?


103

난 그냥 할 수 없어 그것은 버그 나 document.querySelectorAll(...).map(...)파이어 폭스 3.6도, 그리고 내가 교차 게시물을 줄이 블로그에서 SO 질문에 생각 그래서 난 여전히 답을 찾을 수 없습니다 :

http://blowery.org/2008/08/29/yay-for-queryselectorall-boo-for-staticnodelist/

어레이를 얻지 못하는 기술적 이유를 아는 사람이 있습니까? 아니면 StaticNodeList는 사용자가 사용할 수있는 것과 같은 방식으로 배열에서 상속하지 않는 이유 map, concat등?

(원하는 하나의 기능인 경우 다음과 같은 작업을 수행 할 수 있습니다 NodeList.prototype.map = Array.prototype.map;.하지만이 기능이 처음에 차단 된 이유는 무엇입니까?)


3
실제로 getElementsByTagName은 Array가 아니라 컬렉션을 반환하며, Array처럼 사용하려면 (concat 등의 메서드 포함) 루프를 수행하여 이러한 컬렉션을 Array로 변환하고 각 요소를 복사해야합니다. 컬렉션을 배열로. 아무도 이것에 대해 불평하지 않았습니다.
Marco Demaio 2010 년

답변:


81

나는 그것이 W3C의 철학적 결정이라고 믿습니다. W3C DOM [spec]의 디자인은 DOM이 플랫폼과 언어 중립적 인 것을 의미 하기 때문에 JavaScript 디자인과 매우 직교합니다 .

" getElementsByFoo()returns an ordered NodeList"또는 " querySelectorAll()returns a StaticNodeList" 와 같은 결정 은 매우 의도적이므로 구현시 언어 종속 구현 (예 : .mapJavaScript 및 Ruby의 배열에서 사용 가능하지만 C #의 목록에 없음 ).

W3C의 목표는 낮습니다 : 그들은 모든 구현이 적어도 그것을 지원할 수 있다고 믿기 때문에 a NodeListunsigned long 유형readonly .length속성을 포함해야 한다고 말할 것 입니다 . 그러나 그들은 []위치 요소를 얻기 위해 인덱스 연산자가 오버로드되어야 한다고 명시 적으로 말하지는 않을 것입니다. 그들은 구현하고 getElementsByFoo()싶지만 연산자 오버로딩을 지원할 수없는 불쌍한 언어를 방해하고 싶지 않기 때문 입니다. 이것은 스펙의 많은 부분에 걸쳐 존재하는 널리 퍼진 철학입니다.

John Resig는 귀하 와 유사한 옵션 을 제시 했으며 다음과 같이 덧붙였습니다 .

내 주장은 NodeIteratorDOM과 같지 않은 것이 아니라 JavaScript와 같지 않다는 것입니다. JavaScript 언어에있는 기능을 활용하지 않고 최대한의 기능을 사용합니다.

나는 다소 공감한다. DOM이 JavaScript 기능을 염두에두고 특별히 작성 되었다면 사용하기가 훨씬 덜 어색하고 직관적 일 것입니다. 동시에 나는 W3C의 디자인 결정을 이해합니다.


감사합니다. 상황을 이해하는 데 도움이됩니다.
Kev

@Kev : 블로그 기사 페이지에서 StaticNodeList를 배열 로 변환하는 방법에 대해 질문하는 귀하의 의견을 보았습니다 . 나는 NodeList/ StaticNodeList를 네이티브 배열 로 변환하는 방법으로 @ mck89의 대답을지지 할 것이지만 IE (8 obv)에서는 JScript 오류로 실패합니다. 이러한 개체는 호스트 / "특별"이기 때문입니다.
Crescent Fresh

사실, 그것이 내가 그를 찬성 한 이유입니다. 다른 사람이 내 +1을 취소했습니다. 호스트 / 스페셜이란 무엇을 의미합니까?
Kev

1
@Kev : 호스팅 된 변수는 "호스트"환경 (예 : 웹 브라우저)에서 제공하는 모든 변수입니다. 예를 들어 document, window등. IE는 종종 정상적인 사용에 맞지 않는 "특별한"(예 : COM 개체)을 구현 Array.prototype.slice.call합니다 StaticNodeList.;)
Crescent Fresh

200

ES2015 (ES6) 스프레드 연산자를 사용할 수 있습니다 .

[...document.querySelectorAll('div')]

StaticNodeList를 항목의 배열로 변환합니다.

다음은 사용 방법에 대한 예입니다.

[...document.querySelectorAll('div')].map(x => console.log(x.innerHTML))
<div>Text 1</div>
<div>Text 2</div>


24
또 다른 방법은 사용하는 것입니다 Array.from을 () :Array.from(document.querySelectorAll('div')).map(x => console.log(x.innerHTML))
마이클 Berdyshev

42

배열 대신 노드 목록을 반환하는 이유를 모르겠습니다. 아마도 getElementsByTagName처럼 DOM을 업데이트 할 때 결과를 업데이트 할 것이기 때문입니다. 어쨌든 간단한 배열로 결과를 변환하는 매우 간단한 방법은 다음과 같습니다.

Array.prototype.slice.call(document.querySelectorAll(...));

다음을 수행 할 수 있습니다.

Array.prototype.slice.call(document.querySelectorAll(...)).map(...);

3
실제로 DOM을 업데이트 할 때 결과를 업데이트하지 않으므로 '정적'입니다. 결과를 업데이트하려면 qSA를 수동으로 다시 호출해야합니다. slice그래도 라인에 +1 .
Kev

1
예, Kev가 말한 것처럼 qSA 결과 집합은 정적이고 getElementsByTagName () 결과 집합은 동적입니다.
joonas.fi

IE8은 표준 모드에서만 querySelectorAll ()을 지원합니다
mbokil

13

Crescent가 말한 것에 추가하기 위해,

원하는 함수 하나만 있으면 NodeList.prototype.map = Array.prototype.map과 같은 작업을 수행 할 수 있습니다.

이러지마! 작동이 보장되지는 않습니다.

JavaScript 또는 DOM / BOM 표준은 NodeList생성자 함수가 global / window속성 으로 존재 하거나에서 NodeList반환 된 속성 querySelectorAll이 상속되거나 프로토 타입이 쓰기 가능하거나 함수 Array.prototype.map가 실제로 NodeList에서 작동하도록 지정하지 않습니다.

NodeList는 '호스트 객체'가 될 수 있습니다 (IE 및 일부 이전 브라우저에서는 하나임). Array방법은 숫자 및 노출 자바 스크립트 '네이티브 객체에서 작동 할 수있는 것으로 정의 length특성을하지만,이 호스트 오브젝트에 대한 작업에 필요하지 않은 것 (그리고 IE에서, 그들은하지 않습니다).

DOM 목록에있는 모든 배열 메서드 (StaticNodeList뿐만 아니라 모두)를 얻지 못하는 것은 성가신 일이지만 신뢰할 수있는 방법은 없습니다. 모든 DOM 목록을 배열로 수동으로 변환해야합니다.

Array.fromList= function(list) {
    var array= new Array(list.length);
    for (var i= 0, n= list.length; i<n; i++)
        array[i]= list[i];
    return array;
};

Array.fromList(element.childNodes).forEach(function() {
    ...
});

1
쏴라, 그건 생각도 못 했어. 감사!
Kev

+1 동의합니다. 그냥 "var array = new Array (list.length)"대신 "var array = []"를 사용하면 코드가 더 짧아 진다고 생각합니다. 그러나 이것을하는 데 문제가있을 수 있다는 것을 알고 있다면 관심이 있습니다.
Marco Demaio 2010 년

@MarcoDemaio : 아니요, 문제 없습니다. new Array(n)JS terp에게 배열이 끝나는 시간에 대한 힌트를 제공합니다. 이를 통해 미리 해당 공간을 할당 할 수 있으며, 어레이가 커짐에 따라 일부 메모리 재 할당을 피할 수 있으므로 잠재적으로 속도가 향상 될 수 있습니다. 그래도 최신 브라우저에서 실제로 도움이되는지 모르겠습니다.
bobince 2010-08-16


2

다음과 같이 간단히 할 수 있다고 생각합니다.

Array.prototype.map.call(document.querySelectorAll(...), function(...){...});

그것은 나에게 완벽하게 작동합니다.


0

이것은 여기에서 다른 사람들이 제안한 다른 가능성의 범위에 추가하고 싶었던 옵션입니다. 이는 지적 재미만을위한 것이며 권장하지 않습니다 .


단지에 대한 재미 그것의, 여기에 "힘"에 방법 querySelectorAll당신에게 무릎을 꿇고 활하려면 :

Element.prototype.querySelectorAll = (function(QSA){
    return function(){
        return [...QSA.call(this, arguments[0])]
    }
})(Element.prototype.querySelectorAll);

이제 그 기능을 모두 밟아 누가 보스인지 보여주는 것이 기분이 좋습니다. 이제 더 나은 것이 무엇인지 모르겠습니다. 완전히 새로운 명명 된 함수 래퍼를 만든 다음 모든 코드에서 이상한 이름 (대부분의 jQuery 스타일)을 사용하거나 위와 같은 함수를 한 번 재정 의하여 나머지 코드를 계속 사용할 수 있습니다. 원래 DOM 메서드 이름을 사용합니다 querySelectorAll.

  • 이러한 접근 방식은 하위 방법의 사용 가능성을 제거합니다.

당신이 솔직히 [당신이 무엇을 알고 있는지]주지 않는 한, 나는 이것을 어떤 식 으로든 추천하지 않을 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.