D3 javascript diferencia entre foreach y each

Respuestas:

177

Primero, .forEach()no es parte de d3, es una función nativa de las matrices javascript. Entonces,

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

Y eso funciona incluso si d3 no está cargado en la página.

A continuación, d3 .each()funciona en las selecciones de d3 (lo que obtienes cuando lo haces d3.selectAll(...)). Técnicamente, puede llamar .forEach()a una selección d3, ya que detrás de escena, una selección d3 es una matriz con funciones adicionales (una de ellas lo es .each()). Pero no deberías hacer eso porque:

  1. Hacerlo no producirá el comportamiento deseado. Saber cómo utilizar .forEach()con una selección de d3 para producir cualquier comportamiento deseado requeriría una comprensión íntima del funcionamiento interno de d3. Entonces, ¿por qué hacerlo, si solo puede usar la parte pública documentada de la API?

  2. Cuando llama .each(function(d, i) { })a una selección d3, obtiene más que solo dy i: la función se invoca de tal manera que la thispalabra clave en cualquier lugar dentro de esa función apunta al elemento DOM HTML asociado con d. En otras palabras, console.log(this)desde adentro function(d,i) {}registrará algo como <div class="foo"></div>o cualquier elemento html que sea. Y eso es útil, porque luego puede llamar a la función en este thisobjeto para cambiar sus propiedades CSS, contenido o lo que sea. Por lo general, usa d3 para establecer estas propiedades, como en d3.select(this).style('color', '#c33');.

La conclusión principal es que, utilizando .each(), tendrá acceso a 3 cosas que necesita: d, thisy i. Con .forEach(), en una matriz (como en el ejemplo del principio) solo obtienes 2 cosas ( dy i), y tendrías que hacer un montón de trabajo para asociar también un elemento HTML con esas 2 cosas. Y eso, entre otras cosas, es la utilidad de d3.

Meetamit
fuente
16
Gracias por escribir una excelente respuesta y por hacerlo sin incluir ninguno de los comentarios innecesarios que son tan comunes en SO ...
Kevin H. Lin
1
Debe haber una advertencia aquí: cuando necesita un alcance diferente para 'esta' palabra clave pero no necesita un dato en su función llamada, la selección [0] .forEach (...) es mucho más conveniente que la selección.each, lo que necesita una solución alternativa 'self = this' en la función principal si 'this' es significativo fuera de simplemente hacer referencia a elementos DOM.
sdupton
El alcance de @sdupton thises una preocupación en muchos escenarios d3 en los que se pasan funciones de orden superior, como por ejemplo selection.style("color", function(d,i) { /* here 'this' is a DOM element */ }). Creo que es en parte la razón por la que las clases d3 (como d3.svg.axispor ejemplo) no usan los prototypemétodos de definición de clases, como una forma de evitar la dependencia de this. Pero no veo cómo se selection[0].forEach(...)evita este problema. ¿No es el mismo problema?
meetamit
1
@meetamit, puede especificar explícitamente el alcance de 'esto' para usarlo en Array.prototype.forEach con un segundo argumento, pasado después de la función que se llamará en cada elemento. Cuando está escribiendo algo parecido a un contenedor orientado a objetos (estoy usando clases ES6), perder el alcance explícito de 'esto' puede ser un fastidio.
sdupton
2
@sdupton, genial: no sabía que .forEachaceptaba un segundo parámetro para el alcance this. Me hizo darme cuenta de que podría usar algo similar para lograr el mismo efecto con d3 .each()utilizando el .bind()método de JavaScript . Por ejemplo, el siguiente alcance voluntad thisa windowy tendrá console.log que: selection.each(function() { console.log(this); }.bind(window)).
Meetamit