ノードリストにforEachがないのはなぜですか?


91

<abbr>要素の内部テキストを変更する短いスクリプトを作成していましたnodelistが、これにはforEachメソッドがありませんでした。私はそれを知っているnodelistから継承していないArrayが、それは次のようには思えないforEach有用な方法を持っているだろうか?私が追加その防止を認識していないです特定の実装に問題があるforEachのではnodelist

注:DojoとjQueryの両方がforEach何らかの形でノードリストを持っていることを知っています。制限により使用できません。


回答:


94

NodeListがすべての主要なブラウザーでforEach()を使用するようになりました

MDNのnodeList forEach()を参照してください。

元の答え

これらの回答はどれも NodeListがArrayを継承しない理由を説明していないため、これを使用してforEach残りのすべてを保持できます。

答えはこのes-discussスレッドにあります。要するに、それはウェブを壊します:

問題は、instanceofがArray.prototype.concatと組み合わされたArrayであることを誤って想定するコードでした。

Googleのクロージャライブラリにバグがあり、これが原因でほとんどすべてのGoogleのアプリが失敗しました。ライブラリはこれが見つかるとすぐに更新されましたが、concatと組み合わせて同じ誤った仮定を行うコードがまだそこにある可能性があります。

つまり、いくつかのコードは次のようなことをしました

if (x instanceof Array) {
  otherArray.concat(x);
} else {
  doSomethingElseWith(x);
}

ただし、concat「実際の」配列(instanceof Arrayではない)は他のオブジェクトとは異なる方法で処理されます。

[1, 2, 3].concat([4, 5, 6]) // [1, 2, 3, 4, 5, 6]
[1, 2, 3].concat(4) // [1, 2, 3, 4]

つまり、上記のコードxは、NodeListがdoSomethingElseWith(x)パスを下る前に壊れたのに対し、その後、otherArray.concat(x)パスを下っていったためx、実際の配列ではなかったため、奇妙なことをしました。

しばらくの間Elements、Arrayの実際のサブクラスであり、「新しいNodeList」として使用されるクラスの提案がありました。ただし、技術的および仕様に関連するさまざまな理由により、まだ実装することができなかったため、少なくとも現時点ではDOM標準から削除されています


11
私への悪い電話のようです。真剣に、私は時々物事を壊すのが正しい決定だと私は思います、特にそれが将来のための健全なAPIを持っていることを意味する場合は特にそうです。また、そのないウェブが近い安定したプラットフォームであることにでもあるように、人々は彼らの2歳のjavascriptのは、もはや期待通りに機能するために使用されている...
JoyalToTheWorld

3
"Breaks the web"!= "Breaks Google stuff"
Matt

なぜArray.prototypeからforEachメソッドを借りないのですか?たとえば、配列をプロトタイプとして追加する代わりに、コンストラクターでthis.forEach = Array.prototype.forEachを実行するか、NodeListに対してforEachを一意に実装するだけですか?
PopKernel

1
注意; このアップデートNodeList now has forEach() in all major browsersは、IEが主要なブラウザではないことを示唆しているようです。うまくいけば、それは一部の人には当てはまりますが、(まだ)私には当てはまりません。
Graham P Heath

58

できるよ

Array.prototype.forEach.call (nodeList, function (node) {

    // Your code here.

} );

3
Array.prototype.forEach.call短縮することができます[].forEach.call
CodeBrauer

7
@CodeBrauer:これは単にを短くするだけではなくArray.prototype.forEach.call、空の配列を作成してそのforEachメソッドを使用しています。
ポールD.ウェイト2017

34

ノードの新しい配列の作成を検討できます。

  var nodeList = document.getElementsByTagName('div'),

      nodes = Array.prototype.slice.call(nodeList,0); 

  // nodes is an array now.
  nodes.forEach(function(node){ 

       // do your stuff here.  

  });

注:これは、ここで作成するノード参照のリスト/配列であり、重複するノードはありません。

  nodes[0] === nodeList[0] // will be true

22
または単にArray.prototype.forEach.call(nodeList, fun)
akuhn

また、forEach関数に次のようなエイリアスを付けることをお勧めしますvar forEach = Array.prototype.forEach.call(nodeList, callback);。今、あなたは単に電話をかけることができますforEach(nodeList, callback);
Andrew Craswell

19

それは2016年だと、決して言うことはありませんNodeListオブジェクトが実装しているforEach最新のクロム(v52.0.2743.116)でメソッドを。

他のブラウザーはまだこれをサポートしていないため(FF 49でテスト済み)、これを本番環境で使用するには時期尚早ですが、これはすぐに標準化されると思います。


2
Operaもこれをサポートしており、サポートはFirefoxのv50で追加され、15/11/16にリリースされる予定です。
Shaggy、2016年

1
実装されていますが、標準の一部ではありません。Array.prototype.slice.call(nodelist).forEach(…)どちらが標準的で、古いブラウザで機能するかは、まだ最善です。
2017

17

要するに、その方法を実装することは設計上の矛盾です。

MDNから:

NodeListでforEachまたはマップを使用できないのはなぜですか?

NodeListは配列と非常によく似ており、Array.prototypeメソッドを使用するのは魅力的です。ただし、これは不可能です。

JavaScriptには、プロトタイプに基づく継承メカニズムがあります。配列インスタンスは、プロトタイプチェーンが次のようになるため、配列メソッド(forEachやmapなど)を継承します。

myArray --> Array.prototype --> Object.prototype --> null (オブジェクトのプロトタイプチェーンは、Object.getPrototypeOfを数回呼び出すことで取得できます)

forEach、mapなどは、Array.prototypeオブジェクトの独自のプロパティです。

配列とは異なり、NodeListプロトタイプチェーンは次のようになります。

myNodeList --> NodeList.prototype --> Object.prototype --> null

NodeList.prototypeにはitemメソッドが含まれていますが、Array.prototypeメソッドは含まれていないため、NodeListsでは使用できません。

ソース:https : //developer.mozilla.org/en-US/docs/DOM/NodeList(下にスクロールして、なぜforEachを使用できないか、NodeListでマップできないのですか?


8
では、リストなので、なぜそのように設計されているのでしょうか。チェーンを作るために彼らを止めたのは何myNodeList --> NodeList.prototype --> Array.prototype --> Object.prototype --> nullですか?
IgorPantović2014年

14

NodeListでforEachを使用する場合は、配列からその関数をコピーするだけです。

NodeList.prototype.forEach = Array.prototype.forEach;

以上で、Arrayと同じように使用できるようになりました。

document.querySelectorAll('td').forEach(function(o){
   o.innerHTML = 'text';
});

4
ただし、基本クラスの変更は、平均的な読者にはそれほど明白ではありません。つまり、一部のコードの奥深くでは、ブラウザのベースオブジェクトに対して行ったすべてのカスタマイズを覚えておく必要があります。オブジェクトが標準から動作を変更したため、MDNドキュメントに依存することはもはや役に立ちません。forEachは借用したアイデアであり、言語定義の一部ではないことを読者が簡単に理解できるように、呼び出し時に明示的にプロトタイプを適用することをお勧めします。上記の@akuhnの回答を参照してください。
スキマ

@スキマは間違った前提で見当違い。この特定のケースでは、指定されたアプローチにより、NodeListが開発者の予想どおりに動作するように修正されます。これは、システムクラスの問題を修正する最も適切な方法です。(NodeListは未完成のようであり、言語の将来のバージョンで修正される予定です。)
Daniel

1
NodeList.forEachが存在するようになったので、これは陽気でシンプルなポリフィルになります。
デイモン

7

ES2015ではforEach、nodeListにメソッドを使用できるようになりました。

document.querySelectorAll('abbr').forEach( el => console.log(el));

MDNリンクを見る

ただし、HTMLコレクションやその他の配列のようなオブジェクトを使用したい場合は、es2015ではArray.from()method を使用できます。このメソッドは、配列のようなオブジェクトまたは反復可能なオブジェクト(nodeList、HTMLコレクション、文字列などを含む)を取り、新しいArrayインスタンスを返します。次のように使用できます。

const elements = document.getElementsByTagName('abbr');
Array.from(elements).forEach( el => console.log(el));

通りArray.from()の方法がshimmableで、あなたはこのようなES5コードでそれを使用することができます

var elements = document.getElementsByTagName('abbr');
Array.from(elements).forEach( function(el) {
    console.log(el);
});

詳細については、MDNページを参照してください。

確認するには、現在のブラウザのサポートを

または

es2015のもう1つの方法は、spread演算子を使用することです。

[...document.querySelectorAll('abbr')].forEach( el => console.log(el));

MDNスプレッドオペレーター

スプレッドオペレーター-ブラウザサポート


2

私の解決策:

//foreach for nodeList
NodeList.prototype.forEach = Array.prototype.forEach;
//foreach for HTML collection(getElementsByClassName etc.)
HTMLCollection.prototype.forEach = Array.prototype.forEach;

1
特に古いバージョンのIEで、プロトタイプを介してDOMの機能を拡張することは、多くの場合お勧めできません(記事)。
KFE

0

NodeListはDOM APIの一部です。JavaScriptにも適用されるECMAScriptバインディングを見てください。http://www.w3.org/TR/DOM-Level-2-Core/ecma-script-binding.html。nodeList、読み取り専用の長さプロパティ、およびノー​​ドを返すitem(index)関数。

答えは、反復する必要があるということです。代替手段はありません。Foreachは機能しません。Java DOM APIバインディングを使用していて、同じ問題があります。


しかし、それが実装されるべきではない特別な理由がありますか?jQueryとDojoの両方が独自のライブラリに実装しています
SnakesとCoffee

2
しかし、それはどのようにデザインの矛盾ですか?
ヘビとコーヒー

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.