D3 javascript Diferença entre foreach e each

89

Qual é a diferença entre forEache eachem D3js?

Kuan
fonte

Respostas:

179

Primeiro, .forEach()não faz parte do d3, é uma função nativa dos arrays javascript. Então,

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

E isso funciona mesmo se d3 não estiver carregado na página.

Em seguida, d3 .each()funciona em seleções d3 (o que você obtém quando você d3.selectAll(...)). Tecnicamente, você pode chamar .forEach()uma seleção d3, já que, nos bastidores, uma seleção d3 é um array com funções extras (uma delas é .each()). Mas você não deve fazer isso porque:

  1. Isso não produzirá o comportamento desejado. Saber como usar .forEach()uma seleção d3 para produzir qualquer comportamento desejado exigiria uma compreensão íntima do funcionamento interno de d3. Então, por que fazer isso, se você pode apenas usar a parte pública documentada da API.

  2. Ao chamar .each(function(d, i) { })uma seleção d3, você obtém mais do que apenas de i: a função é chamada de forma que a thispalavra - chave em qualquer lugar dentro dessa função aponte para o elemento HTML DOM associado d. Em outras palavras, console.log(this)de dentro function(d,i) {}irá registrar algo como <div class="foo"></div>ou qualquer elemento html que seja. E isso é útil, porque então você pode chamar a função neste thisobjeto para alterar suas propriedades CSS, conteúdo ou qualquer outra coisa. Normalmente, você usa d3 para definir essas propriedades, como em d3.select(this).style('color', '#c33');.

A principal takeaway é que, usando .each()você tem acesso a 3 coisas que você precisa: d, thise i. Com .forEach(), em um array (como no exemplo do início) você obtém apenas 2 coisas ( de i), e você teria que fazer um monte de trabalho para também associar um elemento HTML com essas 2 coisas. E isso, entre outras coisas, é como o d3 é útil.

Meetamit
fonte
17
Obrigado por escrever uma ótima resposta, e por fazer isso sem incluir nenhuma das perguntas desnecessárias que são tão comuns no SO ...
Kevin H. Lin
1
Deve haver uma advertência aqui: quando você precisa de escopos diferentes para 'esta' palavra-chave, mas não precisa de dados na função chamada, selection [0] .forEach (...) é muito mais conveniente do que selection.each, que necessita de uma solução alternativa 'self = this' na função pai se 'this' for significativo fora de meramente fazer referência a elementos DOM.
sdupton
O escopo de @sdupton para thisé uma preocupação em muitos cenários d3 onde você passa em funções de ordem superior, incluindo, por exemplo selection.style("color", function(d,i) { /* here 'this' is a DOM element */ }). Acredito que seja parcialmente por isso que as classes d3 (como d3.svg.axispor exemplo) não usam os prototypemétodos de definição de classes - como uma forma de evitar a dependência de this. Mas não vejo como selection[0].forEach(...)evitar esse problema. Não é o mesmo problema?
encontro de
1
@meetamit você pode definir explicitamente o escopo 'this' para uso em Array.prototype.forEach com um segundo argumento, passado após a função a ser chamada em cada elemento. Quando você está escrevendo qualquer coisa semelhante a um wrapper orientado a objeto (estou usando classes ES6), perder o escopo explícito de 'this' pode ser uma chatice.
sdupton
2
@sdupton, legal - eu não sabia .forEachaceitar um segundo parâmetro para escopo this. Isso me fez perceber que você poderia usar algo semelhante para obter o mesmo efeito com o d3 .each()usando o .bind()método javascript . Por exemplo, o seguinte escopo vontade thispara windowe console.log-lo: selection.each(function() { console.log(this); }.bind(window)).
encontro de