Сравнение разных способов конкатенаций на JavaScript
Однажды проходя собеседование меня спросили
"Если вы будете соединять несколько строчек, то как лучше это сделать?"
и дали примерно такой код "a"+"b"+"c"+...
с намеком на то, что каждая отдельная конкатенация будет кушать ресурсы.
И мне стало интересно как же в действительности будет лучше объединять строки на JS.
Начало измерений
Я решил, что хорошо бы все подходы для конкатенации строчек, которые я смогу придумать, проверить на практике.
Выбрал площадку для тестирования jsben.ch
Подготовка окружения для измерений
Добавил в setup block код для генерации различных строчек
const testData =
[...new Array(1000)]
.map((item, i) => [i+"1234567890"+Math.random(),i+"1a2s3d4f5g6h7j8kl9q0"+Math.random()]);
Конкатенация через Array.join
Вот простая ситуация, есть множество строк в массиве и есть обычной метод join. Этот метод рекомендуется довольно часто в разнообразных уважаемых источниках.
const test = (arr) => {
if (!arr || arr.length == 0) return "";
const result = [""];
for (const item of arr) {
result.push("- ");
result.push(item[0]);
result.push("
");
result.push("- ");
result.push(item[1]);
result.push("
");
}
result.push("
");
return result.join('');
};
test(testData);
Конкатенация через Array.join с применением выноса некоторых строчек в константу
Вынос строк в константу довольно логичная, на мой взгляд, идея, ведь теперь не нужно отдельно для каждой такой строчки выделять дополнительное место в памяти и тратить на это ресурсы.
const test = (arr) => {
if (!arr || arr.length == 0) return "";
//выносим часто встречающиеся строки в константы
const dt = "";
const dt_ = " ";
const dd = "";
const dd_ = " ";
const result = [""];
for (const item of arr) {
result.push(dt);
result.push(item[0]);
result.push(dt_);
result.push(dd);
result.push(item[1]);
result.push(dd_);
}
result.push("
");
return result.join('');
};
test(testData);
Конкатенация через Array.join с использованием интерполяции строки
Отлично выглядит/читается и рекомендуется всеми современными манулами
const test = (arr) => {
if (!arr || arr.length == 0) return "";
const result = [""];
for (const item of arr) {
result.push(<dt>${item[0]}</dt><dd>${item[1]}</dd>
);
}
result.push("
");
return result.join('');
};
test(testData);
Конкатенация через бинарный оператор "+"
Старый добрый оператор плюс, используется в JavaScript с начала времен. Не рекомендуется для использования для конкатенации строк. Думаю это связано с ухудшением наглядности кода по сравнению с интерполяцией, а так же оператор плюс может сделать приведение типа и выдать неожиданный результат.
Но у нас всё просто — строчки соединяются со строчками, поэтому проблем быть не должно.
const test = (arr) => {
if (!arr || arr.length == 0) return "";
let result = "";
for (const item of arr) {
result = result + "- " + item[0] + "
- " + item[1] + "
";
}
result = result + "
";
return result;
};
test(testData);
Конкатенация через TypedArray(типизированный массив)
Решил испробовать необычную конкатенацию через использование типизированного массива Uint8Array. Пытался оптимизировать этот код как мог, поэтому получилось очень громоздко и плохо читаемо.
const test = (arr) => {
const enc = (str)=>{
const strChars = new Uint8Array(str.length);
for (let q =0; q < str.length ; q++){
strChars[q] = str.charCodeAt(q);
}
return strChars;
};
if (!arr || arr.length == 0) return "";
const dt = enc("")
const dt_ = enc(" ");
const dd = enc("");
const dd_ = enc(" ");
const fullLength =
arr.reduce((acc, item) =>acc+item[0].length+item[1].length , 0) +
(dt.length + dt_.length + dd.length + dd_.length)*arr.length +
9;
let pos = 0;
const result = new Uint8Array(fullLength);
result.set(enc(""), pos);pos += 4;
for (const item of arr) {
result.set(dt, pos);pos+=dt.length;
result.set(enc(item[0]), pos);pos+=item[0].length;
result.set(dt_, pos);pos+=dt_.length;
result.set(dd, pos);pos+=dd.length;
result.set(enc(item[1]), pos);pos+=item[1].length;
result.set(dd_, pos);pos+=dd_.length;
}
result.set(enc("
"));pos += 5;
const decoder = new TextDecoder("utf-8");
return decoder.decode(result);
};
test(testData);
Итог
Опыты показали, что результаты могут очень сильно различаться в зависимости от используемого компьютера, браузера и его версии.
Например вот такая ситуация произошла при сравнении на разных браузерах FireFox94.0.2(64bit) и Chrome 96.0.4664.45(64bit) тут явно в FireFox есть проблемы с интерполяцией строк
Итак, объявим победителей:
Выиграл, с очень большим отрывом, обычный бинарный оператор "+".
На втором месте идет join с интерполяцией строки
Третье и четвертое место занимает способ который сначала складывает все строки в массив и уже потом делает join для всех строк.
Способ конкатенации через типизированные массивы оказался самым неудачным в плане производительности.
Итоговый результат можно посмотреть(измерить) тут
Привет! Это комментарий.
Чтобы начать модерировать, редактировать и удалять комментарии, перейдите на экран «Комментарии» в консоли.
Аватары авторов комментариев загружаются с сервиса Gravatar.