web-dev-qa-db-ja.com

document.querySelectorAllが実際の配列ではなくStaticNodeListを返すのはなぜですか?

Firefox 3.6でもdocument.querySelectorAll(...).map(...)を実行できないだけでなく、まだ答えが見つからないというバグがあります。そのため、SOの質問にクロスポストすると思いましたこのブログ:

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

配列を取得しない技術的な理由を誰か知っていますか?または、なぜStaticNodeListがmapconcatなどを使用できるように配列から継承しないのですか?

(ところで、必要な機能が1つだけの場合、NodeList.prototype.map = Array.prototype.map;...のようなことができますが、この機能が(意図的に?)最初にブロックされているのはなぜですか?)

87
Kev

私はそれがW3Cの哲学的な決定であると信じています。 W3C DOM [spec]の設計はJavaScriptの設計と非常に直交しています。DOMはmeantプラットフォームおよび言語に中立であるためです。

getElementsByFoo()が順序付きNodeListを返す」または「querySelectorAll()StaticNodeListを返す」などの決定は非常に意図的であるため、実装は言語に基づいて返されたデータ構造を調整する必要がありません。依存する実装(_.map_はJavaScriptおよびRubyの配列で利用可能ですが、C#のリストではnotのように)。

W3Cの狙いは低い:NodeListには 読み取り専用_.length_型unsigned long型のプロパティ を含めるべきだと言うでしょう。なぜなら、すべての実装が少なくともサポートできると信じているからですthatが、_[]_インデックス演算子が位置要素の取得をサポートするためにオーバーロードされるべきであると明示的には言いません。彼らはgetElementsByFoo()の実装を望む貧弱な小さな言語を失望させたくないからですただし、演​​算子のオーバーロードはサポートできません。これは、仕様の大部分に存在する一般的な哲学です。

John Resigには 同様のオプションの音声 があり、それに 彼が追加

私の議論は、NodeIteratorがあまりDOMに似ていないということではなく、JavaScriptにあまり似ていないということです。 JavaScript言語に存在する機能を利用せず、その能力を最大限に活用します...

私はやや共感します。 JavaScript機能を念頭に置いてDOMが具体的に記述されている場合、DOMを使用する方がはるかに扱いにくく、直感的です。同時に、W3Cの設計上の決定を理解しています。

73
Crescent Fresh

ES2015(ES6)を使用できます スプレッド演算子

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

staticNodeListをアイテムの配列に変換します。

以下に使用方法の例を示します。

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

GetElementsByTagNameのように、DOMを更新すると結果が更新されるため、配列ではなくノードリストを返す理由がわかりません。とにかく、その結果を単純な配列に変換する非常に単純な方法は次のとおりです。

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

そして、あなたは次のことができます:

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

クレセントが言ったことに加えて、

必要な関数が1つだけの場合は、NodeList.prototype.map = Array.prototype.mapのようにできます

これをしないでください!動作が保証されていません。

JavaScriptまたはDOM/BOM標準では、NodeListコンストラクター関数がglobal/windowプロパティとしても存在すること、またはNodeListによって返されるquerySelectorAllが指定されていません。それから継承するか、そのプロトタイプが書き込み可能であるか、関数Array.prototype.mapは実際にNodeListで機能します。

NodeListは、「ホストオブジェクト」(およびIEおよび一部の古いブラウザー)であることが許可されています。Arrayメソッドは、 JavaScriptの数値および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() {
    ...
});
12
bobince

あなたは単に次のことができると思います

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

私にぴったりです

2
Max Leps

これは、ここで他の人が提案した他の可能性の範囲に追加したいオプションです。知的楽しみのためだけのものであり、お勧めしません


それのfunだけのために、querySelectorAllをひざまずいてあなたに屈するように「強制」する方法があります:

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

今度は、その機能を全面的に踏み越えて、誰がボスであるかを示すのは良い気分です。今、私はまったく新しいnamed関数ラッパーを作成し、すべてのコードにその奇妙な名前(ほとんどjQueryスタイル)または上記のように関数を1回オーバーライドして、残りのコードで元のDOMメソッド名querySelectorAllを引き続き使用できるようにします。

  • そのようなアプローチは、 サブメソッド の使用の可能性を排除します

あなたが正直に(あなたが知っている)を与えない限り、私はこれをいかなる方法でもお勧めしません。

0
vsync