D3 Javascript Unterschied zwischen foreach und jedem

86

Was ist der Unterschied zwischen forEachund eachin D3js?

Kuan
quelle

Antworten:

173

Erstens .forEach()ist es nicht Teil von d3, sondern eine native Funktion von Javascript-Arrays. So,

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

Und das funktioniert auch, wenn d3 nicht auf der Seite geladen ist.

Als nächstes arbeitet d3 .each()an d3-Auswahlen (was Sie erhalten, wenn Sie d3.selectAll(...)). Technisch gesehen können Sie .forEach()eine d3-Auswahl aufrufen , da eine d3-Auswahl hinter den Kulissen ein Array mit zusätzlichen Funktionen ist (eine davon ist .each()). Aber das solltest du nicht tun, weil:

  1. Dies führt nicht zum gewünschten Verhalten. .forEach()Um zu wissen, wie man mit einer d3-Auswahl arbeitet, um ein gewünschtes Verhalten zu erzeugen, müsste man das Innenleben von d3 genau verstehen. Warum also, wenn Sie nur den dokumentierten öffentlichen Teil der API verwenden können?

  2. Wenn Sie .each(function(d, i) { })eine d3-Auswahl aufrufen , erhalten Sie mehr als nur dund i: Die Funktion wird so aufgerufen, dass das thisSchlüsselwort an einer beliebigen Stelle in dieser Funktion auf das zugeordnete HTML-DOM-Element verweist d. Mit anderen Worten, console.log(this)von innen function(d,i) {}wird so etwas <div class="foo"></div>oder ein beliebiges HTML-Element protokolliert . Und das ist nützlich, denn dann können Sie die Funktion für dieses thisObjekt aufrufen , um seine CSS-Eigenschaften, Inhalte oder was auch immer zu ändern. Normalerweise verwenden Sie d3, um diese Eigenschaften wie in festzulegen d3.select(this).style('color', '#c33');.

Der Haupt zum Mitnehmen ist , dass, indem .each()Sie den Zugriff auf 3 Dinge, die Sie brauchen: d, thisund i. Mit .forEach()einem Array (wie im Beispiel von Anfang an) erhalten Sie nur zwei Dinge ( dund i), und Sie müssten eine Menge Arbeit erledigen, um diesen beiden Dingen auch ein HTML-Element zuzuordnen. Und so ist unter anderem d3 nützlich.

Meetamit
quelle
15
Vielen Dank, dass Sie eine großartige Antwort geschrieben haben und dies getan haben, ohne den unnötigen Snark einzubeziehen, der bei SO so häufig vorkommt ...
Kevin H. Lin
1
Hier sollte es eine Einschränkung geben: Wenn Sie für 'dieses' Schlüsselwort einen anderen Gültigkeitsbereich benötigen, aber in Ihrer aufgerufenen Funktion kein Datum benötigen, ist die Auswahl [0] .forEach (...) viel praktischer als selection.each, Dies erfordert eine Problemumgehung 'self = this' in der übergeordneten Funktion, wenn 'this' außerhalb der bloßen Referenzierung von DOM-Elementen sinnvoll ist.
Sdupton
Das Scoping nach @sdupton thisist in vielen d3-Szenarien ein Problem, in denen Sie Funktionen höherer Ordnung übergeben, einschließlich beispielsweise selection.style("color", function(d,i) { /* here 'this' is a DOM element */ }). Ich glaube, das ist teilweise der Grund, warum d3-Klassen (wie d3.svg.axiszum Beispiel) die prototypeMethoden zum Definieren von Klassen nicht verwenden - um zu vermeiden, dass man sich darauf verlässt this. Aber ich sehe nicht, wie selection[0].forEach(...)dieses Problem vermieden wird. Ist es nicht das gleiche Problem?
Treffen am
1
@meetamit Sie können 'this' explizit für die Verwendung in Array.prototype.forEach mit einem zweiten Argument festlegen, das nach der Funktion übergeben wird, die für jedes Element aufgerufen werden soll. Wenn Sie etwas schreiben, das einem objektorientierten Wrapper ähnelt (ich verwende ES6-Klassen), kann es eine Schande sein, den expliziten Umfang von "dies" zu verlieren.
Sdupton
2
@sdupton, cool - Ich wusste nicht, dass ich .forEacheinen zweiten Parameter für das Scoping akzeptiert habe this. Mir wurde klar, dass Sie etwas Ähnliches verwenden können, um mit d3s den gleichen Effekt zu erzielen, .each()indem Sie die .bind()Methode von Javascript verwenden. Zum Beispiel kann der folgende Wille Umfang thiszu windowwerden und console.log es: selection.each(function() { console.log(this); }.bind(window)).
Meetamit