6

Изучаю JavaScript. Задачка из учебника:
Напишите функцию checkSpam(str), которая возвращает true, если строка str содержит „html“ или „css“, а иначе false. Функция должна быть нечувствительна к регистру:

Ответ из учебника:

function checkSpam(str) {
  var lowerStr = str.toLowerCase();

return !!(~lowerStr.indexOf('html') || ~lowerStr.indexOf('css')); }

alert( checkSpam('hTml now') ); alert( checkSpam('free cSs') ); alert( checkSpam("more java") );

Вопросы:

  1. Меня путает наличие сразу !! и ~
    Как читается строка return? "Если не найдено, то вернуть ..."
    первый знак ! = Не, и приводим к логическому типу; дальше непонятки из-за ~ и второго отрицания.
  2. Вот мой вариант, он мне кажется понятнее:

function checkSpam(str) {
 var lowerStr = str.toLowerCase();
 return (lowerStr.indexOf('html') != -1 || lowerStr.indexOf('css') != -1);
}
alert( checkSpam('hTml now') );
alert( checkSpam('free cSs') );
alert( checkSpam("more java") );

Есть ли в данных вариантах разница и какой вид более предпочтительный?

3 Answers3

7

Знак ! обозначает не.

Если вы напишите return (~lowerStr.indexOf('html') || ~lowerStr.indexOf('css')), то оно просто выведет положение слов.
Если вы введёте return !(~lowerStr.indexOf('html') || ~lowerStr.indexOf('css')), но оно выведет true/false, при том, что false выведется, если слово было найдено
А когда вы вводите два !!, то оно выведет true, там где было false и наоборот

Знак ~ возвращает значение -(число + 1).
Т.е. в вашем случае, если положение равно -1, то она выводит 0 и при этом в условии выводится false, а если другое любое число, то это true

Yuri
  • 15,957
  • Пример разберем alert( checkSpam('free cSs') ); После ~lowerStr.indexOf('css') получаем -6 = true, поскольку нам в задаче нужно true или false мы переводим в булево первым знаком отрицания, но вместе с тем, true становится false и для этого нужен второй знак отрицания и мы вновь меняем false на true. Все верно? Кажется я просто ваши слова повторил немного по-другому. – Александр Казаков Feb 09 '17 at 21:00
  • 1
    @AlexandrKazakov, нет. Если без ~, то оно выведет -1, как true, а если предложение будет начинаться с нужного слова, то оно покажет 0 и оно выведет false. Но да, если мы получим положение -6, то оно получит true, потом переведёт его в false и потом обратно в true – Yuri Feb 09 '17 at 21:05
  • Вот мой пример `function checkSpam(str) { var lowerStr = str.toLowerCase();

    return (~lowerStr.indexOf('html') || ~lowerStr.indexOf('css')); } alert( checkSpam('free cSs') );` минус шесть выдает

    – Александр Казаков Feb 09 '17 at 21:08
  • @AlexandrKazakov, и что? Значит слово начинается с положения 5 – Yuri Feb 09 '17 at 21:09
  • Значит вы или свой комментарий раньше изменили и там не было про -6, или я просмотрел. Еще вопрос: если indexOf не находит подстроку, то цепочка получается такая: получаем false после первого ! получаем true, после второго ! вновь false и он идет в alert. Все верно? – Александр Казаков Feb 09 '17 at 21:16
  • 1
    @AlexandrKazakov, если строка не найдена, то оно показывает -1, тильда переводит это в 0, первый знак ! выводит true, а второй знак ! выводит false – Yuri Feb 09 '17 at 21:25
6

Вы столкнулись с "магией операторов". В данном случае операторы применяются для следующего:

  1. Оператор ~ возвращает 0 для -1 и не 0 для всего остального. Поэтому в булевом контексте оператор ~ делает почти то же самое, что и сравнение != -1.

  2. Оператор !! (на самом деле это два оператора), который вы правильно назвали двойным отрицанием, преобразует любое значение в булево. Этот оператор нужен только потому что оператор ~ возвращает число, если бы там было сравнение - то и двойное отрицание бы не понадобилось.

Таким образом, вы совершенно правильно написали return (lowerStr.indexOf('html') != -1 || lowerStr.indexOf('css') != -1);. Ваш вариант делает в точности то же самое, что и строка из учебника, только написан понятнее.

Можно даже убрать скобки:

return lowerStr.indexOf('html') != -1 || lowerStr.indexOf('css') != -1;

Именно так и надо писать если ваш код будет читать кто-то, кроме вас самих. Если же вы пишите так называемый "write-only" код - иногда имеет смысл использовать "магические" сокращения, коли это ускоряет его написание.

Pavel Mayorov
  • 58,537
0

Не только, но и ~~undefined === 0

function add (a, b) {
  var res = '', c = 0
  a = a.split('')
  b = b.split('')
  while (a.length || b.length || c) {
    c += ~~a.pop() + ~~b.pop()
    res = c % 10 + res
    c = c > 9
  }
  return res
}

add('63829983432984289347293874', '90938498237058927340892374089')

Здесь две тильды заменяет (parseInt(a.pop()) || 0). Круто, на мой взгляд.