Почему крошечное переупорядочение операций чтения/записи DOM вызывает огромную разницу в производительности

следующий код иллюстрирует проблему, изменение порядка чтения/записи приводит к большой разнице во времени выполнения (протестировано с использованием Chrome, Firefox и IE):

// read->write->read->write...
function clearSlow(divs){
    Array.prototype.forEach.call(divs, function(div) {
        contents.push(div.clientWidth);
        div.style.width = "10px";
    });
}
// read->read->...->write->write...
function clearFast(divs){
    Array.prototype.forEach.call(divs, function(div) {
        contents.push(div.clientWidth);
    });
    Array.prototype.forEach.call(divs, function(div) {
        div.style.width = "10px";
    });
}

Здесь JSFiddle для полного примера http://jsfiddle.net/Dq3KZ/2/.

Мои результаты для n = 100:
Медленная версия: ~ 35 мс
Быстрая версия: ~ 2 мс

для n = 1000:
Медленная версия: ~ 2000 мс
Быстрая версия: ~ 25 мс

Я думаю, что это связано с количеством переходов браузера в каждом случае. В медленном сценарии происходит перезарядка для каждой операции записи. Однако в быстром сценарии переполнение происходит только один раз в конце. Но я не уверен, и я не понимаю, почему это работает (когда операции независимы).

Изменить: Я использовал свойство InnerText вместо clientWidth и Style.Width, у меня такое же поведение при использовании Google Chrome (http://jsfiddle.net/pindexis/CW2BF/7/). Однако, когда используется InnerHTML, почти нет разницы (http://jsfiddle.net/pindexis/8E5Yj/).

Edit2: Я открыл дискуссию о проблеме innerHTML/innerText для интересующихся: почему замена InnerHTML на innerText вызывает > Снижение производительности на 15%

Ответ 1

Операции не являются независимыми.

Должна произойти перепланировка, когда изменения стиля или содержимого изменяются в позициях или размерах и, либо потому, что вы запрашиваете измерение, либо потому, что экран должен быть обновлен (когда ваш код заканчивается).

div.clientWidth - вызов скрытой функции. Когда вы запрашиваете это значение, вы на самом деле запрашиваете актуальное значение, и, таким образом, по мере изменения стиля вы вызываете немедленное оплавление.

В "быстром" случае нет причин перепланировки, пока экран не будет перерисован или вы не запросите размер. В этом случае действительно нужен только один возврат.