Чому + так погано для конкатенації?


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
@Neal, я згоден - я хочу знати, хто всі ці містичні "всякі", здається, у всіх цих питаннях.
GrandmasterB

Добре використовувати + для конкатенації. Використання динамічного набору тексту в порядку. Використання обох в одній мові погано ^ H ^ H ^ H призводить до неоднозначності.
Геррі

6
@Neal - "Усі" будуть Дугласом Крокфордом. Він перелічує це як "жахливе" у своїй книзі "JavaScript: хороші частини".
kirk.burleson

Відповіді:


68

Розглянемо цей фрагмент коду JavaScript:

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

Це ввійде в журнал

результат 1020

Що, швидше за все, не є тим, що було призначено, і може бути важким для відстеження помилок.


4
Дуже приємна ілюстрація основної проблеми: конкатенація рядків не є чітко визначеною, коли типи (string + 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"
комунікативна

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 );

Дасть неінтуїтивні результати, тому що підказка повертає рядкове значення вхідного даних.

№3

Потреба вводити parseFloatабо Number()щоразу, коли я хочу робити доповнення, є втомливою і дратівливою. Я вважаю себе досить розумним, щоб пам'ятати, щоб не зіпсувати свої динамічні типи, але це не означає, що я ніколи не псував свої динамічні типи . Правда полягає в тому, що я занадто лінивий набирати текст parseFloatабо Number()кожен раз робити доповнення, тому що я роблю багато доповнень.

Рішення?

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

Будь-який символ може бути використаний для об'єднання рядків, але більшість з них вже використовується, і найкраще уникати дублювання. Якби я мав свій шлях, я, ймовірно, використовував би _для з'єднання рядків і заборонив ім'я _змінних.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.