D3 javascript Разница между foreach и каждым

Ответы:

178

Во-первых, .forEach()это не часть d3, это встроенная функция массивов javascript. Так,

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

И это работает, даже если d3 не загружен на страницу.

Затем d3 .each()работает с выбором d3 (что вы получаете, когда вы d3.selectAll(...)). Технически вы можете вызвать .forEach()выделение d3, поскольку за кулисами выделение d3 представляет собой массив с дополнительными функциями (одна из них есть .each()). Но этого делать не стоит, потому что:

  1. Это не приведет к желаемому поведению. Знание того, как использовать .forEach()с выбором d3 для получения любого желаемого поведения, потребует глубокого понимания внутренней работы d3. Так зачем это делать, если можно просто использовать документированную общедоступную часть API.

  2. Когда вы вызываете .each(function(d, i) { })выбор d3, вы получаете больше, чем просто dи i: функция вызывается так, что thisключевое слово в любом месте этой функции указывает на элемент HTML DOM, связанный с d. Другими словами, console.log(this)изнутри function(d,i) {}будет регистрироваться что-то вроде <div class="foo"></div>или любой другой элемент html. И это полезно, потому что тогда вы можете вызвать функцию для этого thisобъекта, чтобы изменить его свойства CSS, содержимое или что-то еще. Обычно вы используете d3 для установки этих свойств, как в d3.select(this).style('color', '#c33');.

Основная идея состоит в том, что, используя .each()вы получите доступ к 3 вещи , которые нужны: d, thisи i. С .forEach(), в массиве (как в примере с самого начала) вы получаете только 2 вещи ( dи i), и вам придется проделать кучу работы, чтобы также связать элемент HTML с этими двумя вещами. И в этом, помимо прочего, и полезен d3.

Meetamit
источник
17
Спасибо, что написали отличный ответ, и за то, что сделали это без ненужной шутки, которая так распространена на SO ...
Кевин Х. Лин
1
Здесь должно быть предостережение: когда вам нужна другая область видимости для ключевого слова this, но вам не нужны данные в вызываемой функции, selection [0] .forEach (...) намного удобнее, чем selection.each, что требует обходного пути 'self = this' в родительской функции, если 'this' имеет смысл за пределами простой ссылки на элементы DOM.
sdupton 08
@sdupton scoping для thisявляется проблемой во многих сценариях d3, где вы передаете функции более высокого порядка, включая, например selection.style("color", function(d,i) { /* here 'this' is a DOM element */ }). Я считаю, что отчасти поэтому классы d3 ( d3.svg.axisнапример, такие) не используют prototypeметоды определения классов - как способ избежать зависимости от this. Но я не понимаю, как selection[0].forEach(...)этого избежать. Разве это не та же проблема?
meetamit
1
@meetamit вы можете явно указать this для использования в Array.prototype.forEach со вторым аргументом, передаваемым после функции, вызываемой для каждого элемента. Когда вы пишете что-то похожее на объектно-ориентированную оболочку (я использую классы ES6), потеря явного определения области видимости this может стать неприятностью.
sdupton 08
2
@sdupton, круто - я не знал, что .forEachпринял 2-й параметр для определения объема this. Это заставило меня понять, что вы можете использовать что-то подобное для достижения того же эффекта с d3, .each()используя .bind()метод javascript . Например, следующая будет сфера , thisчтобы windowи console.log его: selection.each(function() { console.log(this); }.bind(window)).
meetamit