D3 javascript Różnica między foreach i each

88

Jaka jest różnica między forEachiw eachD3js?

Kuan
źródło

Odpowiedzi:

178

Po pierwsze, .forEach()nie jest częścią d3, jest to natywna funkcja tablic javascript. Więc,

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

I to działa nawet jeśli d3 nie jest załadowany na stronie.

Następnie d3 .each()działa na selekcjach d3 (co otrzymujesz, gdy ty d3.selectAll(...)). Technicznie rzecz biorąc, możesz wywołać .forEach()selekcję d3, ponieważ za kulisami selekcja d3 to tablica z dodatkowymi funkcjami (jedną z nich jest .each()). Ale nie powinieneś tego robić, ponieważ:

  1. Nie spowoduje to pożądanego zachowania. Wiedza o tym, jak używać .forEach()selekcji d3 w celu wywołania dowolnego pożądanego zachowania, wymagałaby dogłębnego zrozumienia wewnętrznego działania d3. Po co więc to robić, jeśli możesz po prostu skorzystać z udokumentowanej, publicznej części interfejsu API.

  2. Kiedy wywołujesz .each(function(d, i) { })selekcję d3, otrzymujesz coś więcej niż tylko di i: funkcja jest wywoływana w taki sposób, że thissłowo kluczowe w dowolnym miejscu wewnątrz tej funkcji wskazuje na powiązany element HTML DOM d. Innymi słowy, console.log(this)od wewnątrz function(d,i) {}zarejestruje coś takiego <div class="foo"></div>lub jakikolwiek inny element html. Jest to przydatne, ponieważ wtedy możesz wywołać funkcję na tym thisobiekcie, aby zmienić jego właściwości CSS, zawartość lub cokolwiek innego. Zwykle używasz d3 do ustawiania tych właściwości, jak w d3.select(this).style('color', '#c33');.

Głównym wynos jest to, że za pomocą .each()można uzyskać dostęp do 3 rzeczy, które trzeba: d, thisi i. W .forEach()przypadku tablicy (jak w przykładzie od początku) otrzymujesz tylko 2 rzeczy ( di i), i będziesz musiał wykonać sporo pracy, aby również powiązać element HTML z tymi 2 rzeczami. I to między innymi jest przydatne w d3.

meetamit
źródło
17
Dziękuję za napisanie świetnej odpowiedzi i za zrobienie tego bez dodawania niepotrzebnych warkoczy, które są tak powszechne w SO ...
Kevin H. Lin
1
Powinno być tutaj zastrzeżenie: jeśli potrzebujesz innego zakresu dla słowa kluczowego „this”, ale nie potrzebujesz danych w wywołanej funkcji, wybór [0]. ForEach (...) jest znacznie wygodniejszy niż selection.each, co wymaga obejścia „self = this” w funkcji nadrzędnej, jeśli „this” ma znaczenie poza zwykłym odwoływaniem się do elementów DOM.
sdupton
Zakres @sdupton dla thisjest problemem w wielu scenariuszach d3, w których przekazujesz funkcje wyższego rzędu, w tym na przykład selection.style("color", function(d,i) { /* here 'this' is a DOM element */ }). Uważam, że po części dlatego klasy d3 (jak d3.svg.axisna przykład) nie używają prototypemetod definiowania klas - jako sposobu na uniknięcie polegania na this. Ale nie widzę, jak selection[0].forEach(...)uniknąć tego problemu. Czy to nie ten sam problem?
Meetamit
1
@meetamit możesz jawnie określić zakres „this” do użycia w Array.prototype.forEach z drugim argumentem, przekazanym po funkcji, która ma zostać wywołana w każdym elemencie. Kiedy piszesz coś przypominającego opakowanie zorientowane obiektowo (używam klas ES6), utrata wyraźnego zakresu „this” może być kłopotliwa.
sdupton
2
@sdupton, spoko - nie wiedziałem, że .forEachzaakceptowałem drugi parametr do określania zakresu this. Uświadomiłem sobie, że możesz użyć czegoś podobnego do osiągnięcia tego samego efektu z d3, .each()używając .bind()metody javascript . Na przykład, następujący zakres wola this, aby windowi będzie go console.log: selection.each(function() { console.log(this); }.bind(window)).
meetamit