dr.Brain

doctor Brain

мир глазами веб-разработчика

JavaScript: чем опасен плюс?

как простое сложение может привести к ошибкам веб-приложения

dr.Brain

время чтения 4 мин.

Photo by David Clode on Unsplash

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


Основы

Итак, оператор + в JavaScript используется в двух случаях:

  1. для сложения чисел,
  2. для объединения строк.

Вот пример использования этого оператора для сложения:

const price = 20;
const shipping = 3;
const tax = 3''

const total = price + shipping + tax;
console.log(total); // результат - 26

А так оператор + применяется для объединения строк:

const firstName = 'Marty';
const lastName = 'McFly';

const fullName = firstName + ' ' + lastName;
console.log(fullName); // результат - 'Marty McFly'

Кажется, оба метода работают весьма корректно, но что произойдет, если если мы сложим два числа в строковом формате:

console.log('2' + '2'); // результат - '22'

Мы видим, что поведение оператора зависит от типа данных. То есть, когда все входные данные являются числами, происходит математическое сложение. Но когда хотя бы одно и предоставляемых значений - строка, мы получаем конкатенацию:

console.log(2 + '2'); // результат - '22'

Двойная сущность оператора + может приветси к большим проблема в JavaScript-приложениях. Давайте посмотрим, как это происходит.

Пример из реальной жизни

Примеры, приведенные выше, носят весьма общий характер. В реальной жизни проблема носит скрытый и поэтому достаточно сложный характер. Рассмотрим более реалистичную ситуацию:

У Вашей компании есть интернет-магазин, и Вам поставили задачу создать промежуточный индикатор, который будет показывать в шапке сайта текущему пользователю, какую сумму он уже потратил.

Промежуточный результат является суммой стоимости всех товаров в пользовательской корзине. Данные о товарах предоставляются в JSON формате через RestAPI и являются массивами объектов (object literal):

[
  {
    "id": 23173128,
    "title": "Nutella Hazelnut Spread",
    "price": 8
  },
  {
    "id": 89123012,
    "title": "Oreo Chocolate Sandwich",
    "price": 5
  }
]

Вы можете подсчитать общую стоимость товаров в корзине с помощью оператора + в комбинации с методом reduce массива:

const subtotal = productsInCart
  .reduce((total, product) => total + product.price, 0)

Все будет отлично работать. Но однажды произойдут изменения в API, и цены будут предоставлены не в числовом, а в строчном формате:

{
  {
    "id": 23173128,
    "title": "Nutella Hazelnut Spread",
    "price": "8" // Price is now a string!
  },
  {
    "id": 89123012,
    "title": "Oreo Chocolate Sandwich Cookies",
    "price": "5"
  }
}

Код по прежнему будет работать, но теперь сложение 8 и 5 даст результат 85, а не 13, как Вы ожидали. При этом сообщения об ошибке не появится.

Как раз в этом и кроется главная опасность.


Как избежать проблемы?

Если вы хотите сложить числа, над которыми не имеете абсолютного контроля (например, числа, полученные через API или из пользовательских форм), хорошей практикой будет принудительное приведение значений к числовому формату с помощью parseInt, parseFloat, Number:

console.log(parseInt('2') + parseInt('2')) // результат - 4

Так, ошибка, которая является угрозой в примере с интернет-магазином, может быть предотвращена, если использовать функцию parseFloat:

const subtotal = productsInCart
  .reduce((total, product) => total + parseFloat(product.price), 0)

C другой стороны, если данные должны быть представлены в строковом формате, лучше принудитель привести значения к строке:

const prefix = 777;
const suffix = 888;

const result = prefix.toString() + suffix.toString();
console.log(result); // результат - '777888'

Кроме того, использование оператора + для конкатенации строк уже не является хорошей практикой. Лучше воспользоваться шаблонными строками (template literal), которые появились в ES6:

const prefix = 777;
const suffix = 888;

const result = `${prefix}${suffix}`;
console.log(result); // результат - '777888'

Выводы

Оператор + в JavaScript используется как для математического сложения, так и для объединения строк. Если, Вы не отнесетесь к этому факту с должной осторожностью, двойная природа оператора + может привести к нежелательным трудно определяемым оишбкам.

Для профилактики ошибок пользуйтесь принудительным приведением значений к нужному формату:

  1. для чисел: методы parseInt, parseFloat, Number,
  2. для строк: метод toString или шаблонные строки (template literal).

Спасибо за внимание.

Новые публикации

Далее

Категории

О нас

Frontend & Backend. Статьи, обзоры, заметки, код, уроки.