[] .ForEach.call () JavaScript'te ne yapar?


135

Bazı kod parçacıklarına bakıyordum ve boş bir diziye uygulanan forEach ile bir düğüm listesi üzerinden bir işlevi çağıran birden çok öğe buldum.

Örneğin şöyle bir şeyim var:

[].forEach.call( document.querySelectorAll('a'), function(el) {
   // whatever with the current node
});

ama nasıl çalıştığını anlayamıyorum. Biri bana forEach'in önündeki boş dizinin davranışını ve nasıl callçalıştığını açıklayabilir mi?

Yanıtlar:


204

[]bir dizidir.
Bu dizi hiç kullanılmıyor.

Sayfaya yerleştiriliyor, çünkü bir dizi kullanmak size dizi prototiplerine erişmenizi sağlar .forEach.

Bu sadece yazmaktan daha hızlı Array.prototype.forEach.call(...);

Sonra, forEachgirdi olarak bir işlevi alan bir işlev ...

[1,2,3].forEach(function (num) { console.log(num); });

... ve içindeki her eleman için this(burada thisdizi benzeri, a'ya sahip olduğu lengthve bölümlerine erişebileceğiniz gibi this[1]) üç şey aktaracak:

  1. dizideki öğe
  2. öğenin dizini (üçüncü öğe geçer 2)
  3. diziye bir başvuru

Son olarak, .callfonksiyonların sahip olduğu bir prototiptir (diğer fonksiyonlarda çağrılan bir fonksiyondur).
.callilk argümanını alır ve thisnormal fonksiyonun içini callilk argüman olarak geçirdiğiniz şeyle değiştirir ( undefinedveya günlük JS'de nullkullanacak windowveya "katı modda" ise geçtiğiniz her şey olacaktır). Değişkenlerin geri kalanı orijinal işleve aktarılacaktır.

[1, 2, 3].forEach.call(["a", "b", "c"], function (item, i, arr) {
    console.log(i + ": " + item);
});
// 0: "a"
// 1: "b"
// 2: "c"

Bu nedenle, forEachişlevi çağırmanın hızlı bir yolunu oluşturuyorsunuz thisve boş diziden tüm <a>etiketlerin bir listesine geçiyorsunuz ve her bir <a>sırayla, sağlanan işlevi çağırıyorsunuz.

DÜZENLE

Mantıksal Sonuç / Temizleme

Aşağıda, işlevsel programlama girişimlerini bir kenara atmamızı ve her seferinde manuel, satır içi döngüye bağlı kalmamızı öneren bir makale bağlantısı var, çünkü bu çözüm hack-ish ve çirkin.

O süre söyleyebilirim .forEach, benzerlerine göre daha az yararlı olduğunu .map(transformer), .filter(predicate), .reduce(combiner, initialValue)ya erişirken, gerçekten yapmak isteyen tüm, dış dünyayı (değil dizi), n-kere değiştirmek olduğunda, hala amaca hizmet arr[i]veya i.

Öyleyse, Motto açıkça yetenekli ve bilgili bir adam olduğu için eşitsizlikle nasıl başa çıkacağız ve ne yaptığımı / nereye gittiğimi bildiğimi hayal etmek istiyorum (şimdi ve sonra ... ... diğer kez kafa ilk öğrenme)?

Cevap aslında oldukça basit ve Bob Amca ve Sör Crockford'un gözetim nedeniyle yüz yüze geleceği bir şey:

temizle .

function toArray (arrLike) { // or asArray(), or array(), or *whatever*
  return [].slice.call(arrLike);
}

var checked = toArray(checkboxes).filter(isChecked);
checked.forEach(listValues);

Şimdi, bunu kendiniz yapmanız gerekip gerekmediğini soruyorsanız, cevap hayır olabilir ...
Bu kesin şey ... ... bugünlerde üst düzey özelliklere sahip her (?) Kütüphane tarafından yapılıyor.
Lodash veya alt çizgi veya hatta jQuery kullanıyorsanız, hepsinin bir dizi öğeyi alıp n kez bir eylem gerçekleştirme yolu olacaktır.
Böyle bir şey kullanmıyorsanız, elbette, kendiniz yazın.

lib.array = (arrLike, start, end) => [].slice.call(arrLike, start, end);
lib.extend = function (subject) {
  var others = lib.array(arguments, 1);
  return others.reduce(appendKeys, subject);
};

ES6 (ES2015) ve Ötesi için Güncelleme

Bir slice( )/ array( )/ etc yardımcı yöntemi, listeleri tıpkı dizileri kullandıkları gibi (olması gerektiği gibi) kullanmak isteyenler için hayatı kolaylaştırmakla kalmaz, aynı zamanda görece yakınların ES6 + tarayıcılarında çalıştırma lüksüne sahip kişiler için de hayatı kolaylaştırır. Gelecek ya da bugün Babel'de "transpiling" için, bu tür şeyleri gereksiz kılan yerleşik dil özelliklerine sahipsiniz.

function countArgs (...allArgs) {
  return allArgs.length;
}

function logArgs (...allArgs) {
  return allArgs.forEach(arg => console.log(arg));
}

function extend (subject, ...others) { /* return ... */ }


var nodeArray = [ ...nodeList1, ...nodeList2 ];

Süper temiz ve çok kullanışlı.
Look up istirahat ve Yayılması operatörleri; bunları BabelJS sitesinde deneyin; teknoloji yığınınız sıralıysa, bunları Babel ile üretimde ve bir oluşturma adımında kullanın.


Diziye olmayan diziden dönüşümü kullanmak mümkün değil iyi bir neden yoktur ... ... sadece hiçbir şey yapmadan Kodunuzun karmaşa yapmazlar ama her yerde aynı çirkin çizgi olduğunu yapıştırarak.


Şimdi biraz kafam karıştı çünkü yaptıysam: console.log (this); Her zaman pencere nesnesini alacağım Ve .call'ın bunun değerini değiştirdiğini düşündüm
Muhammad Saleh

.call yok değerini değiştirmek thiskullandığınız yüzden, callforeach ile. ForEach görünüyor isterseniz forEach (fn) { var i = 0; var len = this.length; for (; i < length; i += 1) { fn( this[i], i, this ); }değiştirerek ardından thisdiyerek forEach.call( newArrLike )o kadar o yeni bir nesne kullanacağını araçları this.
Norguard

2
@MuhammadSaleh .calldeğerini değiştirmez thisne geçmek iç forEach, .calldeğerini değiştirir this forEach iç . Çağrılmasını istediğiniz fonksiyona neden thisgeçilmediği konusunda kafanız karıştıysa , JavaScript'teki forEachdavranışına bakmalısınız this. Zeyilname, uygulanabilir sadece burada (ve harita / filtre / azaltmak) gibi, ikinci bir argüman var forEach, hangi setleri thisişlevi içinde arr.forEach(fn, thisInFn)veya [].forEach.call(arrLike, fn, thisInFn);da sadece kullanım .bind; arr.forEach(fn.bind(thisInFn));
Norguard

1
@thetrystero tam olarak konu bu. []sadece bir dizi olarak oradadır, böylece yöntemi yapabilir .callve üzerinde çalışmak istediğiniz kümeyi .forEachdeğiştirebilirsiniz this. .callşuna benziyor: içinde geçtiğin şeye eşit olacak function (thisArg, a, b, c) { var method = this; method(a, b, c); }iç kara büyü olması dışında . thismethodthisArg
Norguard

1
yani kısaca ... Array.from ()?
zakius

54

querySelectorAllYöntem, bir döner NodeListbir dizi benzer olan, ancak oldukça büyük bir dizi değil. Bu nedenle, bir forEachyöntemi yoktur (dizi nesneleri aracılığıyla miras alınır Array.prototype).

A NodeListbir diziye benzediğinden, dizi yöntemleri aslında onun üzerinde çalışacaktır, bu nedenle [].forEach.callsizi kullanarak Array.prototype.forEachyöntemi NodeList, sanki basitçe yapabiliyormuşsunuz gibi , bağlamında çağırırsınız yourNodeList.forEach(/*...*/).

Boş dizi değişmezinin, muhtemelen oldukça sık göreceğiniz genişletilmiş sürüme giden bir kısayol olduğunu unutmayın:

Array.prototype.forEach.call(/*...*/);

7
Yani, netleştirmek için, hiçbir avantajı kullanarak var [].forEach.call(['a','b'], cb)üzerinde ['a','b'].forEach(cb)diziden benzeri yok yapılara üzerinde yineleme çalışırken sadece zaman, standart dizilerle gündelik uygulamalarında forEachkendi prototipin? Bu doğru mu?
Matt Fletcher

2
@MattFletcher: Evet bu doğru. Her ikisi de işe yarayacak ama neden şeyler aşırı karmaşık? Yöntemi doğrudan dizinin kendisinde çağırmanız yeterlidir.
James Allardice

Harika, teşekkürler. Ve neden bilmiyorum, belki sadece sokak kredisi için, ALDI'deki yaşlı kadınları etkilemek gibi. Sadık kalacağım [].forEach():)
Matt Fletcher

var section = document.querySelectorAll(".section"); section.forEach((item)=>{ console.log(item.offsetTop) }) iyi çalışıyor
daslicht

@daslicht, IE'nin eski sürümlerinde değil! (Bu do Ancak desteklemek forEachNodeList nesneleri diziler üzerinde değil)
Djave

21

Diğer cevaplar bu kodu çok iyi açıkladı, bu yüzden sadece bir öneri ekleyeceğim.

Bu, basitlik ve netlik için yeniden düzenlenmesi gereken iyi bir kod örneğidir. Bunu kullanmak yerine [].forEach.call()veya Array.prototype.forEach.call()bunu her yaptığınızda basit bir işlev yapın:

function forEach( list, callback ) {
    Array.prototype.forEach.call( list, callback );
}

Artık daha karmaşık ve belirsiz kod yerine bu işlevi çağırabilirsiniz:

forEach( document.querySelectorAll('a'), function( el ) {
   // whatever with the current node
});

5

Kullanılarak daha iyi yazılabilir

Array.prototype.forEach.call( document.querySelectorAll('a'), function(el) {

});

Yaptığı şey, document.querySelectorAll('a')bir diziye benzer bir nesne döndürmektir, ancak bu Arraytürden miras almaz . Bu nedenle , bağlamı forEacholan Array.prototypenesneden yöntemi, tarafından döndürülen değer olarak çağırıyoruz.document.querySelectorAll('a')


4
[].forEach.call( document.querySelectorAll('a'), function(el) {
   // whatever with the current node
});

Temelde şununla aynıdır:

var arr = document.querySelectorAll('a');
arr.forEach(function(el) {
   // whatever with the current node
});

2

Bu eski soruyu güncellemek istiyorum:

[].foreach.call()Modern tarayıcılarda öğeler arasında döngü yapmak için kullanmanın nedeni çoğunlukla sona ermiştir. document.querySelectorAll("a").foreach()Doğrudan kullanabiliriz .

NodeList nesneleri, genellikle Node.childNodes gibi özellikler ve document.querySelectorAll () gibi yöntemler tarafından döndürülen düğüm koleksiyonlarıdır.

NodeList bir Array olmasa da, forEach () ile üzerinde yineleme yapmak mümkündür . Ayrıca Array.from () kullanılarak gerçek bir Array'e dönüştürülebilir.

Ancak, bazı eski tarayıcılar NodeList.forEach () veya Array.from () uygulamamıştır. Bu, Array.prototype.forEach () kullanılarak aşılabilir - bu belgenin Örneğine bakın.


1

Boş bir dizinin forEachprototipinde Function nesnesi olan bir özelliği vardır. (Boş dizi, forEachtüm Arraynesnelerin sahip olduğu işleve bir başvuru elde etmenin kolay bir yoludur .) İşlev nesneleri callde bir işlev olan bir özelliğe sahiptir. Bir İşlevin callişlevini çağırdığınızda , işlevi verilen bağımsız değişkenlerle çalıştırır. İlk argüman thisçağrılan işlevde olur.

Sen belgelerine bulabilirsiniz callfonksiyonu burada . İçin Belgeler forEacholduğunu burada .


1

Sadece bir satır ekleyin:

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

Ve voila!

document.querySelectorAll('a').forEach(function(el) {
  // whatever with the current node
});

Zevk almak :-)

Uyarı: NodeList, global bir sınıftır. Halk kütüphanesi yazıyorsanız bu öneriyi kullanmayın. Bununla birlikte, web sitesinde veya node.js uygulamasında çalışırken öz yeterliliği artırmak için çok uygun bir yoldur.


1

Her zaman kullandığım hızlı ve kirli bir çözüm. Prototiplere dokunmam, iyi bir uygulama kadar. Elbette, bunu daha iyi hale getirmenin birçok yolu var, ama siz anladınız.

const forEach = (array, callback) => {
  if (!array || !array.length || !callback) return
  for (var i = 0; i < array.length; i++) {
    callback(array[i], i);
  }
}

forEach(document.querySelectorAll('.a-class'), (item, index) => {
  console.log(`Item: ${item}, index: ${index}`);
});

0

[]her zaman yeni bir dizi döndürür, eşdeğerdir new Array()ancak bir dizi döndürmesi garantilidir, çünkü Arraykullanıcı tarafından üzerine yazılabilir oysa []olamaz. Bu, prototipini elde etmenin güvenli bir yoludur Array, daha sonra açıklandığı gibi call, işlevi dizi benzeri düğüm listesinde (bu) çalıştırmak için kullanılır.

Verilen bu değere ve bağımsız değişkenlere sahip bir işlevi çağırır. mdn


[]Aslında her zaman bir dizi döndürecek olsa da, window.Arrayherhangi bir şeyle (tüm eski tarayıcılarda) window.Array.prototype.forEachüzerine yazılabilir , böylece herhangi bir şeyle de üzerine yazılabilir. Her iki durumda da alıcıları / ayarlayıcıları veya nesne kapatmayı / dondurmayı (donma kendi genişletilebilirlik sorunlarına neden olur) desteklemeyen tarayıcılarda güvenlik garantisi yoktur.
Norguard

Üzerine yazma olasılığı ile ilgili ekstra bilgi eklemek için cevabını düzenlemek için çekinmeyin prototypeveya 's yöntemleri: forEach.
Xotic750

0

Norguard NE yaptığını açıkladı [].forEach.call()ve James Allardice NEDEN yaptığımızı: çünkü querySelectorAll NodeList, forEach yöntemi olmayan bir döndürür ...

Chrome 51+, Firefox 50+, Opera 38, Safari 10 gibi modern bir tarayıcınız yoksa.

Değilse, bir Polyfill ekleyebilirsiniz :

if (window.NodeList && !NodeList.prototype.forEach) {
    NodeList.prototype.forEach = function (callback, thisArg) {
        thisArg = thisArg || window;
        for (var i = 0; i < this.length; i++) {
            callback.call(thisArg, this[i], i, this);
        }
    };
}

0

Bu sayfada pek çok iyi bilgi (bkz. Cevap + cevap + yorum ), ancak son zamanlarda OP ile aynı soruyu sormuştum ve resmin tamamını görmek için biraz araştırma yapılması gerekti. İşte kısa bir versiyon:

Amaç, bu yöntemlere sahip olmayan Arraybir dizi benzeri yöntemler kullanmaktır NodeList.

Daha eski bir kalıp, Array'in yöntemlerini Function.call()yazmanın []daha kısa Array.prototypeolması yerine bir dizi literal ( ) kullanarak birlikte seçti :

[].forEach.call(document.querySelectorAll('a'), a => {})

Daha yeni bir model (ECMAScript 2015 sonrası) Array.from()şunları kullanmaktır :

Array.from(document.querySelectorAll('a')).forEach(a => {})
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.