JavaScript Foreach:Komplexní průvodce pro začátečníky

Přímo k věci! Co přesně je forEach v JavaScriptu, odkud pochází a jaké jsou případy použití – včetně toho, jak jej použít na „pole podobné“ objekty?

Na konci této příručky najdete odpovědi na tyto otázky.

Pokud jste obeznámeni s polem, pojďme se podívat na toto:

const lists = ['item1', 'item2', 'item3']

A pokud console.log(lists) nebo jednoduše přidejte řádek do své konzole, získáte svá data. To je docela přímočaré.

Výše uvedený zápis se nazývá array literal a jeho použití je velmi jednoduché.

Ale interně, JavaScript engine místo toho používá vestavěný Array() funkce konstruktoru takto:

const lists = new Array('item1', 'item2', 'item3')

Toto je ekvivalentní zápis objektu.

Pokud nahradíte doslovný zápis výše uvedeným, uvidíte stejnou strukturu v konzole.

Nyní, když půjdete o krok dále a zkontrolujete tento konstruktor Array, najdete prototype vlastnost sestávající z několika metod. Pojďme se na to rychle podívat.

Zadejte Array.prototype. v konzole najdete forEach vedle jiných metod:

Odtud to pochází.

OK. Pokud víte, jak vlastnost prototype funguje v OOP, všechny metody v ní definované včetně forEach jsou zděděné a dostupné pro instanci objektu. V tomto případě lists pole.

To znamená, že to můžeme volat přímo na lists pole takto:

lists.forEach()

Co tedy přesně je ForEach?

ForEach je jedním z prostředků smyčkování nebo iterace přes pole. V moderním JavaScriptu se běžně používá místo tradiční smyčky for.

Podívejme se na jeho syntaxi:

forEach(callback(currentElement, index, arr), thisValue)

Obdrží callback funguje jako argument a provede jej pro každý prvek v poli. Tato funkce zpětného volání přijímá tři argumenty – aktuální prvek (který je povinný), jeho index a pole, ke kterému prvek patří – tj. arr .

Také thisValue parametr (pokud je zadán) bude použit jako hodnota this ve zpětném volání.

To je ono, podívejme se na to v praxi!

Začneme jednoduchou smyčkou for takže máte pohled na to, jak smyčky fungují. To nám také poslouží jako osvěžení.

Nastavte si tedy základní .html a propojit .js (nebo jednoduše použijte vývojářské nástroje prohlížeče, pokud vám to vyhovuje).

Ve vašem .js soubor, přidejte následující kód:

const lists = ['item1', , 'item2', 'item3']
const newList = []

for (let i = 0; i < lists.length; i++) {
  newList.push(lists[i]);
}

console.log(newList);

Zde procházíme smyčkou lists pole a poté vložení každého iterovaného prvku do newList pole.

Pokud soubor uložíte a zaškrtnete newList v konzole byste měli vidět tento výstup:

["item1", undefined, "item2", "item3"]

Dostáváme undefined hodnota na prvním indexu, lists[1] tj. druhá položka pole.

Podívejme se, jak forEach metoda zpracovává stejnou iteraci.

Nahraďte cyklus for tímto:

const lists = ['item1', , 'item2', 'item3']
const newList = []

lists.forEach(function (list) {
  newList.push(list);
})

console.log(newList);

výstup:

["item1", "item2", "item3"]

Co se děje?

Pomocí forEach říkáme, že „pro každý iterovaný prvek (tj. jednotlivé list ) v lists pole, provedeme určitou funkci.

Funkce opět vkládá každý iterovaný prvek do newList pole. Ale když se dostaneme k druhé položce pole, forEach přeskočí prázdný slot a pokračuje.

Pojďme náš kód dále optimalizovat.

Můžeme to zkrátit pomocí funkce šipky ES6. Pokud přepíšete zpětné volání pomocí funkce šipky, měli byste mít:

const lists = ['item1', , 'item2', 'item3']
const newList = []

lists.forEach((list) => newList.push(list))
console.log(newList);

Uložte a znovu navštivte konzolu. Mělo by to fungovat perfektně.

Dobrý. To je skvělý začátek.

Udělejme krok dále použitím dalších volitelných parametrů zpětného volání.

Jednoduše přidejte následující kód do .js soubor:

let numbers = [2, 4, 6, 8, 10];

numbers.forEach((number, index, arr) => {
  arr[index] = number * 2; // arr = [2, 4, 6, 8, 10]
})

console.log(numbers);

Jako obvykle forEach prochází smyčkou přes numbers pole a provedení funkce zpětného volání pro každý prvek. V tomto zpětném volání vše, co děláme, je aktualizace numbers pole vynásobením jeho aktuálního iterovaného prvku číslem 2.

A na pole a jeho indexy odkazujeme pomocí arr[index] .

Uložte soubor.

výstup:

[4, 8, 12, 16, 20]

Jdeme dál.

Použití druhého argumentu metody forEach – tj. thisValue

Někdy můžete pracovat s this klíčové slovo ve vašem forEach smyčka. A pokud toto klíčové slovo znáte, budete vědět, že může odkazovat na jiný objekt.

Chcete-li toto klíčové slovo spojit s vaším objektem zájmu, JavaScript forEach nám poskytuje thisValue argument, jak je uvedeno v jeho syntaxi.

Podívejme se na případ použití.

Začněte přidáním následujícího kódu do .js soubor:

function MyNumber() {
  this.data = [];
}

MyNumber.prototype.multiply = function () {
  console.log("test");
}

const num = new MyNumber()

num.multiply();

Pokud jste někdy psali objektově orientovaný styl kódu, měli byste být obeznámeni s výše uvedeným.

Definovali jsme funkci konstruktoru MyNumber obsahující data vlastnost a multiply metoda.

V tuto chvíli kód nic moc nedělá. Pokud jej uložíte a zkontrolujete konzoli, uvidíte pouze „testovací“ zprávu.

Nyní aktualizujme kód, abyste měli:

function MyNumber() {
  this.data = [];
}

MyNumber.prototype.multiply = function (numbers) {
  numbers.forEach(function (number) {
    console.log(this);
    this.data.push(number * 2)
  })
}

const num = new MyNumber()

num.multiply([2, 4, 6]);
console.log(num.data);

Oblast zaměření je multiply metoda. Jeho funkce přijímá pole jako argument, který procházíme pomocí forEach metoda.

Logika je taková, že chceme aktualizovat prázdné data pole vložením nových prvků pole do něj. Takže musíme odkazovat na data vlastnost pomocí this klíčové slovo v rámci zpětného volání.

Ale pokud soubor uložíte a podíváte se do konzole, uvidíte něco takového:

Kromě chyby konzoly se také zobrazuje Window objekt, protože jsme console.log(this) uvnitř forEach .

To znamená, že this odkazuje na globální objekt, kterým je Window . Místo toho chceme this odkazovat na aktuální instanci objektu.

Zde je druhý argument forEach Stačí přidat this jako argument a uložte soubor. Měl bys být dobrý.

numbers.forEach(function (number) {
  console.log(this);
  this.data.push(number * 2)
}, this)

Pokud konzoli znovu zkontrolujete, uvidíte, že this nyní ukazuje na instanci objektu.

Výstup:

[4, 8, 12]

Použití funkce šipky jako zpětného volání

Můžete se vyhnout použití this jako druhý parametr forEach Pokud nahradíte její funkci zpětného volání funkcí šipky. Jako tak:

numbers.forEach((number) => {
  console.log(this);
  this.data.push(number * 2)
})

Uložte a otestujte svůj kód. Bude to fungovat, protože funkce šipky lexikálně váže this hodnota – tj. hodnota this klíčové slovo je určeno jeho kontextem nebo okolním rozsahem.

ForEach() vždy vrací hodnotu undefined

Na to musíte být opatrní, protože je snadné zapomenout. Pokud se pokusíte vrátit forEach funkce, získáte undefined hodnotu.

Uvidíme. Přidejte následující kód do .js soubor.

let numbers = [2, 4, 6, 8, 10];

const myNum = numbers.forEach(number => {
  return number * 2
})

console.log(myNum);

Jak můžete vidět, vracíme forEach logiku a přiřazení výsledku do myNum proměnná.

Pokud soubor uložíte a otevřete konzoli, zobrazí se undefined hodnotu.

Pokud byste chtěli něco vrátit, použijte jinou metodu, jako je map(). Má podobnou definici jako forEach .

Vezměme stejný kód a nahradíme forEach s map metoda takhle:

let numbers = [2, 4, 6, 8, 10];

const myNum = numbers.map(number => {
  return number * 2
})

console.log(myNum);

Uložte soubor a znovu navštivte konzolu.

Výstup:

[4, 8, 12, 16, 20]

Na rozdíl od forEach() , map() metoda vrací nové pole obsahující výsledky volání funkce na každém prvku pole.

Práce s objekty podobnými poli

Pokud jste někdy pracovali s HTML DOM, měli byste být obeznámeni s metodami DOM, jako je getElementsByClassName() , getElementsByTagName() a querySelectorAll() .

Tyto metody lze použít ke shromáždění hromady prvků v dokumentu. A buď vrátí HTMLCollection nebo NodeList (oba jsou objekty podobné poli).

V této části se dozvíte, jak tyto objekty iterovat pomocí forEach .

Podívejme se na praktický příklad. Přidejte do svého .html následující soubor:

<ul class="list">
  <li class="list-item">item1</li>
  <li class="list-item">item2</li>
  <li class="list-item">item3</li>
  <li class="list-item">item4</li>
</ul>

Pokud se pokusíte získat všech li prvky pomocí metod DOM, budete mít:

let itemsByClassName = document.getElementsByClassName('list-item')
console.log(itemsByClassName);

Výstup:

HTMLCollection(4) [li.list-item, li.list-item, li.list-item, li.list-item]
0: li.list-item
1: li.list-item
2: li.list-item
3: li.list-item
length: 4
__proto__ : HTMLCollection

NEBO…

let itemsByQuerySelector = document.querySelectorAll('.list-item')
console.log(itemsByQuerySelector);

Výstup:

NodeList(4) [li.list-item, li.list-item, li.list-item, li.list-item]
0: li.list-item
1: li.list-item
2: li.list-item
3: li.list-item
length: 4
__proto__ : NodeList

Z výstupu byste si mysleli, že jde o pole, protože tak vypadají (protože obsahují indexy a vlastnost length). Ale nejsou!

Oba HTMLCollection a NodeList jsou objekty, které vypadají jako pole, tedy Array-like objektů.

To znamená, že většina metod Array dostupných prostřednictvím Array.prototype. nebude na nich k dispozici. Místo toho dědí metody z Object.prototype .

Jak tedy můžeme použít forEach pro procházení li prvky?

Naštěstí NodeList zdědí několik z těchto metod pole, z nichž forEach je jedním z nich. Můžeme tedy iterovat NodeList přímo pomocí forEach metoda takhle:

let itemsByQuerySelector = document.querySelectorAll('.list-item')

itemsByQuerySelector.forEach(item => console.log(item.innerText))

Při zpětném volání zaznamenáváme vnitřní text pro každý z iterovaných prvků.

Výstup:

item1
item2
item3
item4

Pokud uděláte totéž s HTMLCollection , zobrazí se tato chyba:

Uncaught TypeError: itemsByClassName.forEach is not a function

K procházení tohoto typu objektu podobného Array můžeme použít call() metoda. To nám umožňuje použít metodu, která patří jinému objektu.

V našem případě chceme volat forEach metoda dostupná na Array.prototype a poté jej použijte na HTMLCollection .

Váš kód by měl vypadat takto:

let itemsByClassName = document.getElementsByClassName('list-item')

Array.prototype.forEach.call(itemsByClassName, (item) => console.log(item.innerText))

Uložte a zkontrolujte konzolu. Měli byste mít stejný výstup.

Převod objektů podobných poli na pole

Alternativou k procházení objektů podobnými poli je nejprve je transformovat na pole. Můžeme použít metodu nazvanou Array.from() nebo použijte Spread syntaxi ( ) za to.

Pojďme se rychle podívat.

let itemsByClassName = document.getElementsByClassName('list-item')
let itemsArray = Array.from(itemsByClassName)
console.log(itemsArray);

Je to docela přímočaré.

Výstup:

(4) [li.list-item, li.list-item, li.list-item, li.list-item]
0: li.list-item
1: li.list-item
2: li.list-item
3: li.list-item
length: 4
__proto__ : Array(0)

Výsledek je stejný, pokud použijete operátor spread takto:

let itemsByClassName = document.getElementsByClassName('list-item')
let itemsArray = [...itemsByClassName]
console.log(itemsArray);

Syntaxe rozprostření ( ) „rozšíří“ nebo rozšíří objekt podobný poli uvnitř hranatých závorek, [] z něj udělá správné pole.

Nyní můžete použít forEach metodu přímo na poli.

Další příklad objektu podobného poli.

Než to shrneme, můžete se setkat s touto strukturou objektů podobných Array:

const arrayLike = {
  0: 'item1',
  1: 'item2',
  2: 'item3',
  length: 3
};

Na rozdíl od předchozího není tento typ iterovatelný a nemůžete použít syntaxi spreadu k převodu na pole. V tomto případě jednoduše použijete Array.from() jako tak:

const newArray = Array.from(arrayLike)
console.log(newArray);

Výstup:

["item1", "item2", "item3"]

Odtud můžete zavolat forEach metoda na výstupu pro procházení.

Nebo pokud chcete, jednoduše použijte dřívější metodu volání forEach nepřímo pomocí call() metoda takhle:

const arrayLike = {
  0: 'item1',
  1: 'item2',
  2: 'item3',
  length: 3
};

Array.prototype.forEach.call(arrayLike, (item) => console.log(item))

Pokud soubor uložíte a zkontrolujete konzoli, měli byste vidět své položky.

Závěr

Viděli jsme téměř všechny případy použití metody forEach. Od iterace přes jednoduché pole až po práci s objekty podobnými poli a téměř vše, co je mezi tím. Nyní byste měli být schopni jej použít ve svém projektu.

Pokud máte nějaké dotazy, dejte mi prosím vědět prostřednictvím sekce komentářů.

A pokud se vám tento návod líbí, snažte se ho sdílet na webu a sledujte mě na Twitteru pro další aktualizace.