Neden nodelist forEach'e sahip değil?


91

<abbr>Öğelerin iç metnini değiştirmek için kısa bir komut dosyası üzerinde çalışıyordum , ancak bunun nodelistbir forEachyöntemi olmadığını gördüm . Bunun nodelistmiras almadığını biliyorum Array, ancak sahip olunması forEachyararlı bir yöntem gibi görünmüyor mu? Ben değilim, belirli bir uygulama sorunu ekleyerek bu engeller farkında var mıdır forEachiçin nodelist?

Not: Dojo ve jQuery'nin her ikisinin de forEachnodelistleri için bir formda olduklarının farkındayım . Sınırlamalar nedeniyle ikisini de kullanamıyorum.


Yanıtlar:


94

NodeList artık tüm büyük tarayıcılarda forEach () içeriyor

MDN'deki nodeList forEach () öğesine bakın .

Orijinal cevap

Bu yanıtların hiçbiri NodeList'in neden Array'den miras almadığını, dolayısıyla ona sahip olmasına forEachve diğerlerinin hepsine izin verdiğini açıklamıyor .

Cevap bu es-talk başlığında bulunur . Kısacası, web'i bozar:

Sorun, instanceof'un hatalı olarak örneğin Array.prototype.concat ile kombinasyon halinde bir Array olduğu anlamına geldiğini varsayan koddu.

Google'ın Kapatma Kitaplığında, bu nedenle neredeyse tüm Google uygulamalarının başarısız olmasına neden olan bir hata vardı. Kitaplık bulunur bulunmaz güncellenmiştir, ancak yine de concat ile birlikte aynı yanlış varsayımı yapan kodlar olabilir.

Yani, bazı kodlar şöyle bir şey yaptı

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

Ancak, concat"gerçek" dizileri (Array örneğini değil) diğer nesnelerden farklı olarak ele alır:

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

Bu, yukarıdaki kodun xbir NodeList olduğu zaman kırıldığı anlamına gelir , çünkü daha önce doSomethingElseWith(x)yoldan aşağı inerken , daha sonra otherArray.concat(x), xgerçek bir dizi olmadığı için tuhaf bir şey yapan yoldan aşağı indi .

Bir süredir ElementsArray'in gerçek bir alt sınıfı olan ve "yeni DüğümListesi" olarak kullanılacak bir sınıf için bir öneri vardı . Bununla birlikte, çeşitli teknik ve spesifikasyonlarla ilgili nedenlerden dolayı henüz uygulanması mümkün olmadığından, en azından şimdilik DOM Standardından kaldırıldı .


11
Bana kötü bir çağrı gibi görünüyor. Cidden, özellikle gelecekte aklı başında API'lere sahip olduğumuz anlamına geliyorsa, ara sıra bir şeyleri kırmanın doğru karar olduğunu düşünüyorum. Ayrıca, web istikrarlı bir platform olmaya yakın bile değil, insanlar 2 yıllık javascript'in artık beklendiği gibi
çalışmamasına alışıyorlar

3
"Web'i kırıyor"! = "Google ile ilgili şeyleri
Matta

Neden sadece Array.prototype'dan forEach yöntemini ödünç almıyorsunuz? Örneğin prototip olarak Array eklemek yerine, yapıcıda this.forEach = Array.prototype.forEach yapın, hatta sadece forEach'i NodeList için benzersiz olarak uygulayın.
PopKernel

2
Not; Bu güncelleme NodeList now has forEach() in all major browsers, IE'nin büyük bir tarayıcı olmadığını ima ediyor gibi görünüyor. Umarım bu bazı insanlar için doğrudur, ama benim için doğru değil (henüz).
Graham P Heath

58

Yapabilirsin

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

    // Your code here.

} );

3
Array.prototype.forEach.callkısaltılabilir[].forEach.call
CodeBrauer

7
@CodeBrauer: Bu sadece kısaltmak değil Array.prototype.forEach.call, boş bir dizi oluşturmak ve onun forEachyöntemini kullanmaktır .
Paul D. Waite

34

Yeni bir düğüm dizisi oluşturmayı düşünebilirsiniz.

  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.  

  });

Not: Bu, burada oluşturduğumuz düğüm referanslarının bir listesi / dizisidir, yinelenen düğümler yoktur.

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

22
Ya da sadece yap Array.prototype.forEach.call(nodeList, fun).
akuhn

Ben de böyle forEach fonksiyonunu aliasing öneririm: var forEach = Array.prototype.forEach.call(nodeList, callback);. Şimdi basitçe arayabilirsinizforEach(nodeList, callback);
Andrew Craswell

19

Asla asla deme, 2016 yılı ve NodeListnesne forEachen son Chrome'da bir yöntem uyguladı (v52.0.2743.116).

Diğer tarayıcılar henüz bunu desteklemediği için üretimde kullanmak için henüz çok erken (FF 49'da test edildi), ancak bunun yakında standartlaştırılacağını tahmin ediyorum.


2
Opera ayrıca bunu destekliyor ve 15/11/16 tarihinde yayınlanması planlanan Firefox v50'de destek eklenecek.
Shaggy

1
Uygulanmasına rağmen, herhangi bir standardın parçası değildir. Hala Array.prototype.slice.call(nodelist).forEach(…)standart olan ve eski tarayıcılarda çalışan en iyisidir .
Nate

17

Kısacası, bu yöntemi uygulamak bir tasarım çelişkisidir.

MDN'den:

Neden bir NodeList'de forEach veya map kullanamıyorum?

NodeList, dizilere çok benzer ve bunların üzerinde Array.prototype yöntemlerini kullanmak cazip gelebilir. Ancak bu imkansızdır.

JavaScript, prototiplere dayalı bir miras mekanizmasına sahiptir. Dizi örnekleri, prototip zincirleri aşağıdaki gibi göründüğü için dizi yöntemlerini (forEach veya map gibi) devralır:

myArray --> Array.prototype --> Object.prototype --> null (bir nesnenin prototip zinciri, birkaç kez Object.getPrototypeOf çağrılarak elde edilebilir)

forEach, map ve benzerleri, Array.prototype nesnesinin kendi özellikleridir.

Dizilerden farklı olarak, NodeList prototip zinciri aşağıdaki gibi görünür:

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

NodeList.prototype, item yöntemini içerir, ancak Array.prototype yöntemlerinden hiçbirini içermez, bu nedenle NodeLists üzerinde kullanılamazlar.

Kaynak: https://developer.mozilla.org/en-US/docs/DOM/NodeList ( NodeList'de neden forEach veya map kullanamıyorum? Bölümüne gidin )


8
Öyleyse, bir liste olduğuna göre, neden bu şekilde tasarlandı? Ne yapmak zincirine onları durdurmanın edildi: myNodeList --> NodeList.prototype --> Array.prototype --> Object.prototype --> null?
Igor Pantoviç

14

NodeList üzerinde forEach kullanmak istiyorsanız, bu işlevi Array'den kopyalamanız yeterlidir:

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

Hepsi bu, şimdi onu Array için yaptığınız gibi kullanabilirsiniz:

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

4
Bunun dışında, temel sınıfları mutasyona uğratmak, ortalama bir okuyucu için çok açık değildir. Başka bir deyişle, bazı kodların derinliklerinde, tarayıcı temel nesnelerinde yaptığınız her özelleştirmeyi hatırlamanız gerekir. MDN belgelerine güvenmek artık yararlı değildir çünkü nesneler normdan farklı bir davranışa sahiptir. Prototipi arama zamanında açıkça uygulamak daha iyidir, böylece okuyucu forEach'in ödünç alınmış bir fikir olduğunu ve dil tanımının parçası olan bir şey olmadığını kolayca anlayabilir. @Akuhn'un yukarıda verdiği cevaba bakın.
Sukima

@Sukima yanlış tesisler tarafından yanlış yönlendirildi. Bu özel durumda, verilen yaklaşım NodeList'i geliştiricinin beklediği gibi davranması için düzeltir. Bu, sistem Sınıfı sorununu çözmenin en uygun yoludur. (NodeList tamamlanmamış gibi görünüyor ve gelecekteki dil sürümlerinde düzeltilmesi gerekiyor.)
Daniel Garmoshka

1
Artık NodeList.forEach var olduğuna göre, bu çok komik bir şekilde basit bir çoklu dolgu haline geliyor!
Damon

7

ES2015'te, artık forEachnodeList için yöntemi kullanabilirsiniz .

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

MDN Bağlantısına Bakın

Bununla birlikte, HTML Koleksiyonlarını veya diğer dizi benzeri nesneleri kullanmak istiyorsanız, es2015'te Array.from()yöntemi kullanabilirsiniz . Bu yöntem, dizi benzeri veya yinelenebilir bir nesneyi (nodeList, HTML Koleksiyonları, dizeler vb. Dahil) alır ve yeni bir Array örneği döndürür. Bunu şu şekilde kullanabilirsiniz:

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

Gibi Array.from()yöntem shimmable, sen böyle ES5 kodunda kullanabilirsiniz

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

Ayrıntılar için MDN sayfasına bakın.

Mevcut tarayıcı desteğini kontrol etmek için .

VEYA

es2015'in başka bir yolu da yayma operatörünü kullanmaktır.

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

MDN yayılma operatörü

Yayılma Operatörü - Tarayıcı Desteği


2

Çözümüm:

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

1
DOM'un işlevselliğini prototipler aracılığıyla genişletmek, özellikle IE'nin eski sürümlerinde ( makale ) genellikle iyi bir fikir değildir .
KFE

0

NodeList, DOM API'nin bir parçasıdır. JavaScript için de geçerli olan ECMAScript bağlantılarına bakın. http://www.w3.org/TR/DOM-Level-2-Core/ecma-script-binding.html . Bir düğüm döndürmek için nodeList ve salt okunur uzunluk özelliği ve öğe (dizin) işlevi.

Cevap, yinelemelisiniz. Alternatif yok. Foreach işe yaramayacak. Java DOM API bağlamalarıyla çalışıyorum ve aynı sorunu yaşıyorum.


Ancak uygulanmaması için belirli bir neden var mı? Hem jQuery hem de Dojo bunu kendi kütüphanelerinde uyguladılar
Snakes and Coffee

2
ama bu nasıl bir tasarım çatışması?
Yılanlar ve Kahve

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.