[JavaScript] Внимание, подводный камень

Автор Сообщение
news_bot ®

Стаж: 6 лет 3 месяца
Сообщений: 27286

Создавать темы news_bot ® написал(а)
24-Июл-2020 12:35


Я только что нашёл очень незаметный баг в своём коде, и хочу поделиться им.
Задача
Дан список строк: VALID_STRINGS.
Cоздать функцию test(x) которая должна вернуть true, если x — это одна из строк в этом массиве.
Решение №1: Решение в лоб
Самым простым решением, которое может быть — это пройтись по всем строкам в этом массиве и сравнить.
const VALID_STRINGS = [/* VALID STRINGS */]
function test1(x) {
  for (const string of VALID_STRINGS) {
    if (string === x) return  true
  }
  return false
}

Это решение правильное, но медленное, потому что оно заставляет при каждом вызове функции пробегать по массиву в поисках совпадения. Таким образом сложность алгоритма по времени будет O(длина массива VALID_STRINGS)
Решение №2: Словарь
Массив не очень подходящая структура данных для подобной проверки. Куда лучше использовать словарь для хранения валидных строк. Ведь доступ к элементу словаря по ключу выполняется за константное время.
const VALID_STRINGS = [/* VALID STRINGS */]
const VALID_STRINGS_DICT = {}
for (const string of VALID_STRINGS) {
  VALID_STRINGS_DICT[string] = true
}
function test2(x) {
  return VALID_STRINGS_DICT[x] === true
}

Отличное решение за константное время!
Берегись! Подводный камень!
Это хоть и быстрое решение, но не правильное. Оно не гарантирует того, что х — будет элементом массива VALID_STRINGS. И чтобы это продемонстрировать приведу контрпример:
// Предположим
const VALID_STRINGS = ['somestring', 'anotherstring']
// Тогда после заполнения словаря, он будет иметь следующий вид
const VALID_STRINGS_DICT = { somestring: true, anotherstring: true }
const underwaterRock = ['somestring']
test2(underwaterRock) // вернёт true

Хоть underwaterRock и не является строкой — но наша функция вернула true. А всё потому, что внутри тела функции test2(x) происходит использование x в качестве ключа.
VALID_STRINGS_DICT[x]

В этот момент — x приводится к строковому значению. И в этом и есть проблема — массив приводится к строке путем перечисления своих значений через запятую. Но когда это массив из одного строкового значения — он приводится в точности к своему первому элементу.
['somestring'].toString() === 'somestring'

Решение №3: С дополнительной проверкой
Добавим проверку типа прежде чем использовать x в качестве ключа
const VALID_STRINGS = [/* VALID STRINGS */]
const VALID_STRINGS_DICT = {}
for (const string of VALID_STRINGS) {
  VALID_STRINGS_DICT[string] = true
}
function test2(x) {
  return typeof x === 'string' && VALID_STRINGS_DICT[x] === true
}

Дополнительная операция, но результат правильный.
Вывод
Спасибо за внимание, и будьте аккуратны, обращайте внимание на весь спектр входных значений ваших задач.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_javascript, #_javascript, #_bug, #_validation, #_javascript
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 19-Май 09:04
Часовой пояс: UTC + 5