Практика может быть далека от теории — это известно. Программисты, однако, зачастую избегают вредных последствий этого тезиса, т.к. в первую очередь учатся на практике, а уж потом читают книжки), если вообще читают. В общем, я хочу рассмотреть один интересный факт, заявленный автором книги «Professional JavaScript for Web Developers» (Zakas N. C.):
Одна из общеизвестных проблем ECMAScript — это производительность конкатенации строк. Так же как и некоторые другие языки, ECMAScript поддерживает строки неизменяемыми, то есть их значения не могут быть изменены. Рассмотрим следующий код:
str += "world";
Код выполнит следующие шаги, невидимые для разработчика:
1. Создание строки для хранения "hello ".
2. Создание строки для хранения "world".
3. Создание строки для хранения результата конкатенации.
4. Копирование текущего содержания str в результат.
5. Копирование "world" в результат.
6. Обновление str результирующим значением.
Шаги выполняются каждый раз, когда происходит конкатенация, таким образом операция получается очень дорогой. Решение — использовать массив как хранилище данных и затем методом join() создавать результирующую строку. Рассмотрим вместо старого кода новый:
arr[0] = "hello ";
arr[1] = "world";
var str = arr.join("");
В этом случае выполняются следующие шаги:
1. Создание строки для хранения результата.
2. Копирование каждой строки в соответствующее место в результате.
На основании этого автор предлагает следующее масштабируемуе решение проблемы производительности конкатенации строк:
this.__strings__ = new Array;
}
StringBuffer.prototype.append = function (str) {
this.__strings__.push(str);
};
StringBuffer.prototype.toString = function () {
return this.__strings__.join("");
};
И как явный пример использования:
var d1 = new Date();
var str = "";
for (var i=0; i < 10000; i++) {
str += "text";
}
var d2 = new Date();
document.write("Concatenation with plus: " + (d2.getTime() - d1.getTime()) + " milliseconds");
var oBuffer = new StringBuffer();
d1 = new Date();
for (var i=0; i < 10000; i++) {
oBuffer.append("text");
}
var sResult = buffer.toString();
d2 = new Date();
document.write("< br / >Concatenation with StringBuffer: " + (d2.getTime() -
d1.getTime()) + " milliseconds");
При этом он заявил, что тест должен показать экономию порядка 100—200% процентов над использованием оператора сложения +=.
Меня очень впечатлило данное утверждение и я решил его проверить. Результат в Firefox 3 удивил: 5 и 7 миллисекунд соответственно, т.е. с оператором конкатенации, который был явно унижен автором, скрипт выполнялся быстрее. Для сбора статистики число проходов в цикле было увеличено на два порядка. Результат теста в разных браузерах смотрим ниже:
Творение мелкомягких даже не удивило тем, что зависло на цикле из миллиона итераций. Оно же, как получилось, единственное, которое удовлетворяет гипотезе Zakas'а о том, что += выполняется медленнее. Очень хорошую производительность продемонстрировал новый браузер от гугла.
Вывод получился очень простым: производительность оператора += кроссбраузерно не повышается выше описанным трюком. А в некоторых браузерах этот прием даже вреден.
Параметры ПО при проведении теста: Windows XP SP3, IE v.6, v.7, Opera v9,6, Mozilla v.3

Совсем по-другому дела обстоят в ИЕ. Поэкспериментровав в ИЕ7 выяснил, что "свежеоткрытым" он ведет себя вполне достойно в обоих случаях (см. изображение ниже). А вот если понаоткрывать в нем вкладок с нагруженными приложениями (Gmail, GDocs, пару ютьюб-страниц и т.д.) тем самым прилично забив память, то конкатенация += начинает сильно тормозить. Время выполнения цикла в 3000 итераций составляет более 11 секунд (Vista, 2Гб RAM, Pentium Dual 2.12ГГц):
Аналогичных проблем в других браузерах не наблюдается.
Таким образом, сам собой напрашивается вывод либо повсеметно использовать способ Закаса, либо использовать +=, но с проверкой браузера, что будет более трудоемким для разработчика и не факт, что оправданным.
Да и сам способ Закаса кажется громоздким. Ведь всегда проще и понятнее сделать так:
for (i = 0; i < 1000; i++) {
result[result.length] = i; // push не работает в ИЕ5.5 и ниже
}
result = result.join('');
Хотя описанный прием повышает производительность, но также и усложняет код, снизит его читаемость.
В то же время, ведь, JavaScript - клиентский язык, проблемы с производительностью на клиентских машинах обычно стоят не так остро, как для серверных языко.
Оправдано ли всё это?
Этот прием не то что оправдан, он иногда просто необходим, т.к. прямая контатенация в большом цикле вешает браузер, что очень неприятно для пользователя. О проценте Internet Explorer среди используемых браузеров можно не говорить :-)
Вопрос в том, как часто бывает настолько большой цикл - 1000 и более. В реальной JavaScript-жизни часто ли такое бывает?
JavaScript - частный случай ECMAScript?