Почему + так плохо для объединения?

44

Все говорят, что одной из проблем JavaScript является использование +[ примера ] для конкатенации строк. Некоторые говорят, что проблема не в том +, что это приведение типов [см. Комментарии из предыдущего примера]. Но языки со строгой типизацией без проблем используют + для конкатенации и принудительных типов. Например, в C #:

int number = 1;
MyObject obj = new MyObject();

var result = "Hello" + number + obj;
// is equivalent to
string result = "hello" + number.ToString() + obj.ToString();

Так почему же конкатенация строк в JavaScript такая большая проблема?

конфигуратор
источник
17
почему bad. Кто такой Everyone?
Нил
3
Я думаю, что проблема + математический оператор. И что конкатенационная функция + сбивает с толку этот стандартный процесс. потому что в то время как «Hello» + номер может работать номер + «Hello» может не работать.
SoylentGray
3
@ Нил, я согласен - я хочу знать, кто все эти мистические «все», кажется, во всех этих вопросах.
GrandmasterB
Использование + для объединения в порядке. Использование динамического набора текста в порядке. Использование обоих на одном языке плохо ^ H ^ H ^ H приводит к неоднозначности.
Джерри
6
@Neal - "Все" были бы Дугласом Крокфордом. Он перечисляет это как «ужасный» в своей книге «JavaScript: хорошие части».
kirk.burleson

Ответы:

68

Рассмотрим этот фрагмент кода JavaScript:

var a = 10;
var b = 20;
console.log('result is ' + a + b);

Это войдет

результат 1020

Что, скорее всего, не то, что было задумано, и может быть трудно отследить ошибку.

MCHL
источник
4
Очень хорошая иллюстрация основной проблемы: конкатенация строк не является четко определенной, когда типы (строка + int + int в примере) смешиваются. Лучше иметь совершенно другой оператор (например, Perl '.') С четко определенной семантикой.
Брюс Эдигер
9
Или способ Пайтона решения этого, что бы заставить вас обернуть aи bв str(a)и str(b)вызовы; в противном случае вы получите ValueError: cannot concatenate 'str' and 'int'.
Афекс
6
@FrustratedWithFormsDesigner: добавить скобки. 'result is ' + (a + b),
Джон Перди
18
Дело в том, что в C # вы можете сделать то же самое, и вы получите тот же результат - System.Console.WriteLine("result is " + 10 + 20);но никто никогда не жалуется на это в C #. Вот чего я не понимаю - что такого в JavaScript делает его более запутанным?
конфигуратор
7
@configurator: потому что людям говорят, что C # совершенен, а javascript ужасен. Они оба не правы, но репутация языка, кажется, остается неизменной, восприятие очень важно, особенно в интернете.
gbjbaanb
43

Когда вы говорите «плохо», вы имеете в виду «неправильно» или «медленно»? Довод об использовании математических операторов , чтобы сделать конкатенацию, возможно , «неправильным» аргумент, но есть еще один аргумент , чтобы быть , что использование +сделать много из конкатенации может быть очень медленным.

Мы не говорим о "Hello" + numberпроизводительности, мы говорим о создании сравнительно большой строки, многократно добавляя ее в цикл.

var combined = "";
for (var i = 0; i < 1000000; i++) {
    combined = combined + "hello ";
}

В JavaScript (и C # в этом отношении) строки являются неизменяемыми. Они никогда не могут быть изменены, только заменены другими строками. Вы, вероятно, знаете, что combined + "hello "напрямую не изменяете combinedпеременную - операция создает новую строку, которая является результатом объединения двух строк вместе, но вы должны затем назначить эту новую строку combinedпеременной, если вы хотите, чтобы она была изменена.

Итак, этот цикл создает миллион различных строковых объектов и отбрасывает 999 999 из них. Создание такого количества строк, которые постоянно увеличиваются в размере, не быстрое, и теперь сборщику мусора предстоит проделать большую работу для очистки после этого.

C # имеет точно такую ​​же проблему, которая решается в этой среде классом StringBuilder . В JavaScript вы получите гораздо лучшую производительность, создав массив всех строк, которые вы хотите объединить, и затем объедините их один раз в конце, вместо миллиона раз в цикле:

var parts = [];
for (var i = 0; i < 1000000; i++) {
    parts.push("hello");
}
var combined = parts.join(" ");
Джоэл Мюллер
источник
3
Это отличный ответ. Он не только ответил на вопрос, но и дал образование.
Чарльз Спрейберри
3
Это совсем не то, что я имел в виду, это совершенно не связано с вопросом.
конфигуратор
4
Сожалею. Похоже, по крайней мере 7 человек неправильно поняли ваш вопрос.
Джоэл Мюллер
9
По поводу производительности: Возможно, так было в 2011 году, но теперь метод оператора + работает намного быстрее, чем метод array.join (по крайней мере, в v8). Смотрите этот jsperf: jsperf.com/string-concatenation101
UpTheCreek
2
Любая идея, как метод соединения генерирует одну строку из нескольких частей за один шаг, вместо того, чтобы также объединять затем одну за другой, таким образом также генерируя все эти промежуточные строки?
Angelo.Hannes
14

Неплохо использовать + как для сложения, так и для объединения, если только вы можете добавлять только числа и объединять только строки. Тем не менее, Javascript будет автоматически преобразовывать числа и строки по мере необходимости, поэтому у вас есть:

3 * 5 => 15
"3" * "5" => 15
2 + " fish" => "2 fish"
"3" + "5" => "35"  // hmmm...

Возможно, проще говоря, перегрузка "+" при наличии автоматического преобразования типов нарушает ожидаемую ассоциативность оператора +:

("x" + 1) + 3 != "x" + (1 + 3)
Кевин Клайн
источник
2
Также в конкатенации отсутствует коммутативное свойство сложения, 3 + 5 == 5 + 3но"3" + "5" != "5" + "3"
Caleth
10

Типичная проблема, возникающая при конкатенации строк, связана с несколькими проблемами:

  1. +символ перегрузки
  2. простые ошибки
  3. программисты ленивы

# 1

Первая и главная проблема использования +конкатенации и сложения строк заключается в том, что вы используете +конкатенацию и сложение строк .

если у вас есть переменные aи b, и вы установили c = a + b, есть неоднозначность, зависящая от типов aи b. Эта двусмысленность заставляет вас предпринять дополнительные шаги для смягчения проблемы:

Строка Concat:

c = '' + a + b;

Дополнение:

c = parseFloat(a) + parseFloat(b);

Это подводит меня ко второму пункту

# 2

Очень легко случайно привести переменную к строке, не осознавая этого. Также легко забыть, что ваш ввод вводится в виде строки:

a = prompt('Pick a number');
b = prompt('Pick a second number');
alert( a + b );

Будет давать не интуитивные результаты, потому что prompt возвращает строковое значение ввода.

# 3

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

Решение?

Не все языки используют +для конкатенации строк. PHP использует .для объединения строк, что помогает различать, когда вы хотите добавить числа и когда вы хотите объединить строки.

Любой символ может быть использован для объединения строк, но большинство из них уже используются, и лучше избегать дублирования. Если бы у меня был свой путь, я бы, вероятно, использовал _для объединения строк и запретил бы _вводить имена переменных.

zzzzBov
источник