За всеки масив в javascript?

Как мога да видя всички записи в масив, използвайки javascript?

Мислех, че е нещо подобно:

 forEach(instance in theArray) 

Където theArray е моят масив, но изглежда грешен.

4041
17 февр. Dante1986 е на 17 февруари 2012-02-17 16:51 '12 в 16:51 ч. 2012-02-17 16:51
@ 32 отговора
  • 1
  • 2

TL; DR

  • Не използвайте услугата „ for-in ако не я използвате със защита или поне не знаете защо може да ви ухапе.
  • Най-добрите ви залагания обикновено са

    • for-of цикъл (за ES2015 +),
    • Array#forEach ( spec | MDN ) (или some негови роднини и такива) (само за ES5 +)
    • просто старомодно for примки,
    • или вход за вход със защита.

Но все още има какво да се изследва, да се чете на ...


JavaScript има мощна семантика за циклично преобразуване на масиви и обекти от тип масив. Разделих отговора на две части: опции за автентични масиви и опции за неща, които са сходни само с масиви, като обект arguments , други изпълними обекти (ES2015 +), DOM колекции и др.

Бързо забелязвам, че вече можете да използвате опциите ES2015, дори и за двигателите ES5, като изпратите ES2015 на ES5. Намерете "ES2015 transpiling" / "ES6 transpiling" за още ...

Е, вижте нашите опции:

За реални масиви

Имате три опции в ECMAScript 5 ("ES5"), най-широко разпространената в момента версия и още две добавени в ECMAScript 2015 ("ES2015", "ES6"):

  1. Използване forEach и свързани (ES5 +)
  2. Използвайте прости for цикъл
  3. Използвайте правилно for-in
  4. Използвайте for-of (използвайте скрит итератор) (ES2015 +)
  5. Използване на iterator изрично (ES2015 +)

подробности:

1. Използвайте forEach и свързани

Във всяка безкрайно модерна среда (добре, не в IE8), където имате достъп до функциите на Array добавени от ES5 (директно или използвайки пълни пълнежи), можете да използвате forEach ( spec | MDN ):

 var a = ["a", "b", "c"]; a.forEach(function(entry) { console.log(entry); }); 

forEach приема функция за обратно извикване и, по желание, стойност, използвана по this когато се обажда на това обратно извикване (не се използва по-горе). Обратното извикване се извиква за всеки запис в масива, за да пропуснете несъществуващи записи в разредени масиви. Въпреки, че използвах само един аргумент по-горе, обратното извикване се нарича с три: стойността на всеки запис, индекса на този запис и връзката към масива, който повтаряте (в случай, че функцията ви вече не я притежава).

Ако не поддържате наследени браузъри, като например IE8 (които NetApps показва на повече от 4% от пазара към момента на писане през септември 2016 г.), можете щастливо да използвате forEach на универсална уеб страница без оформление на страницата. Ако трябва да поддържате остарели браузъри, лесно е да извършите операция shimming / polyfilling forEach (намерете "es5 shim" за няколко опции).

forEach има предимството, че не е необходимо да декларирате индексиране и стойности на променливи в областта на съдържанието, тъй като те се предоставят като аргументи за функцията за итерация и са толкова добре ориентирани към тази итерация.

Ако сте загрижени за времето, необходимо за изпълнение на извикване на функция за всеки запис на масив, не бъдете; подробности .

В допълнение, forEach е forEach "loop through them all", но ES5 е дефинирала няколко други полезни "разработки чрез функции на масива и неща", включително:

  • every (спира цикъла първи път, когато обратното извикване връща false или нещо невярно)
  • some (спира цикъла първи път, когато обратно извикване връща true или нещо правдоподобно)
  • filter (създава нов масив, който включва елементи, в които функцията на филтъра връща true и пропуска онези, в които връща false )
  • map (създава нов масив от стойности за обратно извикване)
  • reduce (увеличаване на стойност, повторно извикване на обратно извикване, преминаване на предишни стойности, вижте спецификацията за подробности, полезни за сумиране на съдържанието на масив и много други неща)
  • reduceRight (например reduce , но работи в низходящ ред, а не във възходящ ред)

2. Използвайте прост цикъл

Понякога старите начини са най-добри:

 var index; var a = ["a", "b", "c"]; for (index = 0; index < a.length; ++index) { console.log(a[index]); } 

Ако дължината на масива не се променя по време на цикъла и неговата производителност е чувствителен код (малко вероятно), малко по-сложна версия на заснемането с предната дължина може да бъде малко по-бърза:

 var index, len; var a = ["a", "b", "c"]; for (index = 0, len = a.length; index < len; ++index) { console.log(a[index]); } 

И / или броене назад:

 var index; var a = ["a", "b", "c"]; for (index = a.length - 1; index >= 0; --index) { console.log(a[index]); } 

Но със съвременните механизми на JavaScript рядко трябва да изкопаете този последен сок.

В ES2015 и по-висока можете да направите променливите си за индекси и стойности локални for for цикли:

 let a = ["a", "b", "c"]; for (let index = 0; index < a.length; ++index) { let value = a[index]; } //console.log(index); // Would cause "ReferenceError: index is not defined" //console.log(value); // Would cause "ReferenceError: value is not defined" 

И когато правите това, не само value но и index пресъздават за всяка итерация на цикъла, т.е. затварянията, създадени в цикъла на цикъла, съдържат препратка към indexvalue ), създаден за тази конкретна итерация:

 let divs = document.querySelectorAll("div"); for (let index = 0; index < divs.length; ++index) { divs[index].addEventListener('click', e => { alert("Index is: " + index); }); } 

Ако сте имали пет divs, ще получите "Index is: 0", ако сте кликнали на първото и "Index is: 4", ако сте натиснали последния. Това не работи, ако използвате var вместо let .

3. Използвайте правилно.

Ще накарате хората да ви казват да използвате for-in , но това не е за какво for-in . for-in записва чрез изброените свойства на обекта, а не индексите на масива. Поръчката не е гарантирана , дори в ES2015 (ES6). ES2015 определя реда на свойствата на даден обект (използвайки [[OwnPropertyKeys]] , [[Enumerate]] и неща, които ги използват като Object.getOwnPropertyKeys ), но не уточнява, че Object.getOwnPropertyKeys ще следва тази поръчка. (Подробности в този друг отговор .)

Това обаче може да бъде полезно, особено за редки масиви , ако използвате подходящи предпазни мерки:

 // 'a' is a sparse array var key; var a = []; a[0] = "a"; a[10] = "b"; a[10000] = "c"; for (key in a) { if (a.hasOwnProperty(key)  // These are explained /^0$|^[1-9]\d*$/.test(key)  // and then hidden key <= 4294967294 // away below ) { console.log(a[key]); } } 

Отбележете двете проверки:

  1. Че обектът има собствено свойство под това име (а не това, което наследява от прототипа си), и

  2. Това, че ключът е цифров низ от базов номер 10 в обичайната му струна и неговата стойност е <= 2 ^ 32 - 2 (което е 4,294,967,294). Откъде идва този номер? Това е част от дефиницията на индекса на масива в спецификацията . Други числа (нецели числа, отрицателни числа, числа по-големи от 2 ^ 32 - 2) не са индекси на масиви. Причината за 2 ^ 32 - 2 е това, което прави най-високата стойност на индекса по-малка от 2 ^ 32 - 1 , което е максималната стойност на length масива. (Например, дължината на масива съответства на 32-битово беззнаково цяло число.) (Потвърждение от RobG да посочи в коментар на моя блог пост, че предишният ми тест не беше съвсем прав).

Това е малка част от допълнителните разходи за всяка итерация на цикли в повечето масиви, но ако имате рядък масив, това може да е по-ефикасен начин за циклиране, защото това са само цикли за записи, които всъщност съществуват. Например, за масива по-горе, ние цикъл три пъти (за клавишите "0" , "10" и "10000" - помня, това са линии), а не 10,001 пъти.

Сега не искате да го пишете всеки път, така че можете да го поставите в инструментариума си:

 function arrayHasOwnIndex(array, prop) { return array.hasOwnProperty(prop)  /^0$|^[1-9]\d*$/.test(prop)  prop <= 4294967294; // 2^32 - 2 } 

И тогава ще я използваме по следния начин:

 for (key in a) { if (arrayHasOwnIndex(a, key)) { console.log(a[key]); } } 

Или, ако се интересувате само от теста „достатъчно добър за повечето случаи“, можете да го използвате, но докато е близо, това не е съвсем правилно:

 for (key in a) { // "Good enough" for most cases if (String(parseInt(key, 10)) === key  a.hasOwnProperty(key)) { console.log(a[key]); } } 

4. Използвайте for-of (използвайте скрит итератор) (ES2015 +)

ES2015 добавя итератори към JavaScript. Най-лесният начин за използване на итератори е новият оператор. Изглежда така:

 var val; var a = ["a", "b", "c"]; for (val of a) { console.log(val); } 

заключение:

 а б в

Под покрива, който получава итератор от масива и минава през него, получава стойности от него. Той няма никакъв проблем с използването на for-in , защото използва итератор, дефиниран от обект (масив), а масивите определят, че техните итератори повтарят своите записи (а не техните свойства). За разлика от for-in в ES5, редът, в който се разглеждат записите, е численият ред на техните индекси.

5. Използвайте явно итератора (ES2015 +)

Понякога можете изрично да използвате итератор. Можете да направите и това, въпреки че е много по-пасивно от това for-of . Изглежда така:

 var a = ["a", "b", "c"]; var it = a.values(); var entry; while (!(entry = it.next()).done) { console.log(entry.value); } 

Итераторът е обект, който съответства на дефиницията на Iterator в спецификацията. next метод връща нов обект на резултат всеки път, когато го наричате. Резултатният обект има свойство, done , казва ни дали е направено, и value свойството със стойността за тази итерация. ( done задължително, ако е false , value е задължителна, ако не е undefined ).

value варира в зависимост от итератора; масиви поддържат (поне) три функции, които връщат итератори:

  • values() : Това е тази, която използвах по-горе. Тя връща итератор, където всяка value е масив за тази итерация ( "a" , "b" и "c" в примера по-горе).
  • keys() : Връща един итератор, където всяка value е ключът за тази итерация (така че за нашите по-горе ще a "0" , а след това "1" , а след това "2" ).
  • entries() : връща итератор, където всяка value е масив под формата на [key, value] за тази итерация.

За обекти като масив

В допълнение към истински масиви има и подобни на масиви обекти, които имат свойство и свойства за length с числови имена: NodeList инстанции, обект на arguments и др. Как NodeList съдържанието им?

Използвайте някой от горните параметри за масиви.

Най-малко някои и може би повечето или дори всички по-горе масиви често се използват еднакво добре за обекти от типа масив:

  1. Използване forEach и свързани (ES5 +)

    Различните функции на Array.prototype са "умишлено родови" и обикновено могат да бъдат използвани за обекти като масив чрез Function#call Function#apply или Function#apply . (Вж. Отказ от отговорност за обекти, предоставени от хоста в края на този отговор, но това е рядък проблем.)

    Да предположим, че искате да използвате forEach в childNodes Node childNodes . Ще направите това:

     Array.prototype.forEach.call(node.childNodes, function(child) { // Do something with 'child' }); 

    Ако ще направите това много, може да искате да получите копие от функционалната референция в променлива, която ще се използва повторно, например:

     // (This is all presumably in some scoping function) var forEach = Array.prototype.forEach; // Then later... forEach.call(node.childNodes, function(child) { // Do something with 'child' }); 
  2. Използвайте прости for цикъл

    Очевидно е, че for обекти като масив се прилага опростен цикъл.

  3. Използвайте правилно for-in

    for-in със същите гаранции като с масив, трябва да работи с обекти, подобни на масив; Може да се изискват резервации за артикули, предоставени от домакина на номер 1 по-горе.

  4. Използвайте for-of (използвайте скрит итератор) (ES2015 +)

    for-of ще използва итератора, предоставен от обекта (ако има такъв); ще трябва да видим как това се случва с различни обекти, подобни на масиви, особено с хост файлове. Например, спецификацията за NodeList от querySelectorAll актуализирана, за да поддържа итерация. Спецификацията за HTMLCollection на getElementsByTagName не getElementsByTagName била.

  5. Използване на iterator изрично (ES2015 +)

    Вижте номер 4, трябва да видим как се разгръщат итераторите.

Създайте истински масив

В други случаи можете да конвертирате обект като масив в истински масив. За да направите това е изненадващо лесно:

  1. Използвайте метода на slice масив

    Можем да използваме метода slice array, който, както и другите методи, споменати по-горе, е "умишлено генеричен" и следователно може да се използва с подобни на масиви обекти, например:

     var trueArray = Array.prototype.slice.call(arrayLikeObject); 

    Така например, ако искаме да преобразуваме NodeList в реален масив, можем да направим това:

     var divs = Array.prototype.slice.call(document.querySelectorAll("div")); 

    Вижте „Внимание за обектите, предоставени от хоста“ по-долу. По-специално, имайте предвид, че това няма да работи в IE8 и по-рано, което не позволява използването на обекти, предоставени от хоста като this .

  2. Използване на синтаксис за разпространение ( ... )

    Също така е възможно да се използва синтаксисът за разпространение ES2015 с JavaScript механизми, които поддържат тази функция:

     var trueArray = [...iterableObject]; 

    Така например, ако искаме да преобразуваме NodeList в истински масив, със синтаксиса на разпространение, той става доста кратък:

     var divs = [...document.querySelectorAll("div")]; 
  3. Използвайте Array.from (spec) | (MDN)

    Array.from (ES2015 +, но лесно запълнен) създава масив от обект като масив, ако е необходимо, първо да предава записи чрез функцията за съвпадение. така:

     var divs = Array.from(document.querySelectorAll("div")); 

    Или, ако искате да получите масив от имена на етикети за елементи с даден клас, трябва да използвате функцията за картографиране:

     // Arrow function (ES2015): var divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName); // Standard function (since 'Array.from' can be shimmed): var divs = Array.from(document.querySelectorAll(".some-class"), function(element) { return element.tagName; }); 

Внимание за съоръженията, предоставени от домакина

Ако използвате функции Array.prototype с обекти като хостове (DOM списъци и други неща, предоставени от браузъра, а не JavaScript механизма), трябва да се уверите, че сте тествали в целевите си среди, за да се уверите, че предоставеният хост обект се държи е вярно. Повечето от тях се държат правилно (сега), но е важно да се провери. Причината е, че повечето от методите на Array.prototype , които вероятно искате да използвате, разчитат на обекта, предоставен от хоста, давайки честен отговор на абстракцията [[HasProperty]] . По време на това писане, браузърите направиха много добра работа, но спецификацията 5.1 предположи, че предоставеният от хоста обект може да не е честен. Това е в §8.6.2 , няколко параграфа под голямата таблица в началото на този раздел, където се казва:

Приемащите обекти могат да прилагат тези вътрешни методи по всякакъв начин, освен ако не е отбелязано друго; например, една възможност е [[Get]] и [[Put]] за конкретен хост обект наистина да извлекат и запазят стойности на свойство, но [[HasProperty]] винаги генерира false .

(Не можах да намеря еквивалентна формулировка в спецификацията ES2015, но тя все още трябва да бъде.) Отново, както при писането на общ хост на предоставения масив от тип обект в съвременните браузъри [ NodeList екземпляри, например], направи [[HasProperty]] дръжка правилно, но важно проверите.)

6292
17 февр. Отговорът е даден от TJ Crowder 17 февруари. 2012-02-17 16:53 '12 в 4:53 pm 2012-02-17 16:53

Edit : този отговор е безнадеждно остарял. За по-модерен подход, погледнете методите, налични в масива . Интересни методи могат да бъдат:

  • за всеки
  • карта
  • филтъра
  • цип
  • за намаляване
  • всеки
  • малко

Стандартният начин за итерация на масив в JavaScript е for -loop vanilla:

border=0
 var length = arr.length, element = null; for (var i = 0; i < length; i++) { element = arr[i]; // Do something with element } 

Имайте предвид обаче, че този подход е добър само ако имате плътен масив и всеки индекс е зает от елемент. Ако масивът е рядък, тогава с този подход може да се сблъскате с проблеми с производителността, тъй като ще преминете през много индекси, които всъщност не съществуват в масива. В този случай е по-добре да се използва for.. in -loop. Въпреки това, трябва да използвате подходящи предпазни мерки, за да сте сигурни, че са в for..in само необходимите свойства на масива (т.е. елементи на масив), защото за for..in също ще бъдат изброени в стари браузъри или ако допълнителните свойства са дефинирани като enumerable .

В ECMAScript 5 методът forEach ще бъде използван за прототипа на масив, но той не се поддържа от стари браузъри. Следователно, за да можете да го използвате последователно, трябва или да имате среда, която я поддържа (например Node.js за JavaScript от страна на сървъра), или да използвате "Polyfill". Polyfill за тази функционалност обаче е тривиална и тъй като прави кода по-лесен за четене, той трябва да бъде включен в Polyfill.

470
17 февр. Отговор, даден от PatrikAkerstrand на 17 февруари. 2012-02-17 16:55 '12 в 16:55 ч. 2012-02-17 16:55

Ако използвате библиотеката jQuery , можете да използвате jQuery.each :

 var length = yourArray.length; for (var i = 0; i < length; i++) { // Do something with yourArray[i]. } 
214
17 февр. Отговорът е даден на Poonam 17 Feb. 2012-02-17 17:01 '12 в 17:01 2012-02-17 17:01

Върни се обратно

Мисля, че обратното за цикъла заслужава да се спомене тук:

 for (var i = array.length; i--; ) { // process array[i] } 

предимства:

  • Не е необходимо да декларирате временна променлива len или да я сравнявате с array.length при всяка итерация, която може да бъде оптимизирана за минута.
  • Премахването на братя и сестри от DOM в обратен ред обикновено е по-ефективно . (Браузърът трябва да премести по-малко елементи във вътрешните масиви.)
  • Ако промените масив по време на цикъл, на или след индекс I (например, изтривате или вмъквате елемент в array[i] ), тогава директната линия прескача елемент, който се премества наляво, за да позиционира i или повторно проверява i-тия елемент, който е бил изместен вдясно. В традиционния цикъл можете да актуализирате i, за да посочите следващия елемент, който трябва да бъде обработен - 1, но просто промяната на посоката на итерацията често е по-просто и по-елегантно решение .
  • По подобен начин, при промяна или изтриване на вложени DOM елементи, обратната обработка може да заобикаля грешки . Например, помислете за промяна на innerHTML на родителския възел, преди да обработите неговите деца. По времето, когато детето достигне възела, то ще бъде отделено от DOM, заменяйки го с нов потомък, когато е написан родителският html.
  • по-кратки и прочетени от някои други налични опции. Въпреки че губи до forEach() и до ES6 for ... of .

недостатъци:

  • Той обработва елементите в обратен ред. Ако сте построили нов масив от резултатите или сте отпечатали на екрана, изходът, разбира се, ще бъде отменен по отношение на оригиналния ред.
  • Многократното вмъкване на братя и сестри в DOM като тяхното първо дете, за да се запази техният ред, е по-малко ефективно . (В браузере все равно придется перекладывать вещи.) Чтобы создавать узлы DOM эффективно и упорядоченно, просто переходите вперед и добавьте как обычно (а также используйте "фрагмент документа" ).
  • Обратный цикл запутывает для младших разработчиков. (Вы можете считать это преимущество, в зависимости от вашего прогноза.)

Должен ли я всегда использовать его?