Pourquoi une petite réorganisation des opérations de lecture / écriture des DOM entraîne une énorme différence de performance

Le code suivant illustre le problème, la modification de l'ordre de lecture / écriture entraîne une grande différence de temps d'exécution (testé à l'aide de Chrome, Firefox et 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"; }); } 

Voici un jSFiddle pour l'exemple complet http://jsfiddle.net/Dq3KZ/2/ .

Mes résultats pour n = 100:
Version lente: ~ 35 ms
Version rapide: ~ 2ms

Pour n = 1000:
Version lente: ~ 2000 ms
Version rapide: ~ 25ms

Je pense que cela est lié au nombre de refoulement du navigateur dans chaque cas. Dans le scénario lent, une refusion se produit pour chaque opération d'écriture. Cependant, dans le scénario rapide, le reflux ne se produit qu'une seule fois à la fin. Mais je ne suis pas sûr et je ne comprends pas pourquoi cela fonctionne de cette façon (lorsque les opérations sont indépendantes).

Edit: J'ai utilisé la propriété InnerText au lieu de clientWidth et Style.Width , j'ai eu le même comportement lors de l'utilisation de Google Chrome ( http://jsfiddle.net/pindexis/CW2BF/7/ ). Cependant, lorsque InnerHTML est utilisé, il n'y a presque aucune différence ( http://jsfiddle.net/pindexis/8E5Yj/ ).

Edit2: J'ai ouvert une discussion sur le problème innerHTML / innerText pour les personnes intéressées: pourquoi le remplacement de InnerHTML avec innerText provoque> 15X de baisse de performance

Les opérations ne sont pas indépendantes.

Une refusion doit se produire lorsque le style ou les tailles de contenu ont changé et que des positions ou des dimensions sont nécessaires, soit parce que vous demandez une dimension, soit parce que l'écran doit être mis à jour (lorsque votre code est terminé).

div.clientWidth est un appel de fonction caché. Lorsque vous demandez cette valeur, vous demandez en fait une valeur à jour et, à mesure que le style change, vous déclenchez une refusion immédiate.

Dans le cas «rapide», il n'y a aucune raison de refouler jusqu'à ce que l'écran soit redessiné ou que vous demandez une taille. Une seule refusion est vraiment nécessaire dans ce cas.