Читаем JavaScript
шесть мест, где ломается питоновская интуиция
Что это
JavaScript похож на Python ровно настолько, чтобы вводить в заблуждение. Циклы, функции, условия выглядят знакомо — а потом код делает не то, и непонятно почему.
В статье шесть мест, где интуиция подводит. Всё ниже — настоящий вывод node,
а не пересказ документации.
Как выглядит в коде
1. Сортировка чисел сортирует не числа
[10, 9, 1, 100].sort()[ 1, 10, 100, 9 ]По умолчанию sort превращает элементы в строки и сравнивает их как текст.
Для чисел нужна функция сравнения:
[10, 9, 1, 100].sort((a, b) => a - b) // [ 1, 9, 10, 100 ]Ошибка тихая: на числах от 1 до 9 она незаметна, на двузначных — ломается.
2. Два вида равенства
'1' == 1 -> true
'1' === 1 -> false
null == undefined -> true
null === undefined -> false== приводит типы перед сравнением, === — нет. Правило простое: всегда
===. Единственное оправданное исключение — x == null, которое разом
проверяет и null, и undefined.
3. Истинность не та, к которой ты привык
[] -> истина (в Python пустой список ложен!)
{} -> истина
"" -> ложь
"0" -> истина
0 -> ложь
NaN -> ложь
null -> ложь
undefined -> ложьПервая строка ломает больше всего кода. Проверка «есть ли элементы» через
if (items) в JavaScript всегда истинна, даже для пустого массива. Нужно
if (items.length).
4. Плюс склеивает, минус вычитает
1 + '1' -> '11' (склеил строки)
1 - '1' -> 0 (превратил в числа)Отсюда классика: данные пришли из формы или JSON строками, и вместо суммы
получается склейка. '10' + 5 даёт '105'.
5. Два вида «ничего»
| Что значит | |
|---|---|
undefined | значения не было: нет такого ключа, функция ничего не вернула |
null | значение есть, и оно — «ничего» |
Обращение к несуществующему ключу не падает:
user.email -> undefined
user.email?.length -> undefined
user.email ?? 'не указан' -> 'не указан'Это противоположность Python, где такое даёт KeyError. Удобно и опасно
одновременно: ошибка не сообщает о себе, а всплывает дальше — в виде
Cannot read properties of undefined (статья 8.4).
Два оператора, которые встречаются постоянно:
?.— «если слева неnull/undefined, иди дальше, иначе верниundefined»;??— «возьми левое, а если оноnull/undefined, возьми правое».
?? отличается от || тем, что не считает 0 и "" пустотой. Для чисел
это принципиально: temp || 20 превратит ноль градусов в двадцать, а
temp ?? 20 — нет.
6. const не делает объект неизменяемым
const cities = ["Oslo"];
cities.push("Bergen"); // можно
cities = []; // нельзяconst запрещает переприсваивать переменную, но не менять содержимое. То же
самое, что ссылки в Python (статья 2.4).
Как сказать ИИ
Перепиши этот код на строгие сравнения: везде === вместо ==,
проверки массивов через .length, ?? вместо || там,
где значение может быть 0 или пустой строкой.
Поведение не меняй.Этот код пришёл из Python. Что здесь будет работать иначе
из-за особенностей JavaScript? Только список различий.Числа приходят из JSON строками. Где в этом коде может
получиться склейка вместо сложения?На что смотреть
== в сгенерированном коде. Почти всегда должно быть ===.
sort() без функции сравнения на числах.
if (array) вместо if (array.length). Условие всегда истинно.
|| для значений по умолчанию. Ломается на 0 и "". Нужен ??.
var. Устаревшее объявление с неожиданной областью видимости. В новом коде
только let и const (статья 3.1, причина 3).
Числа из строк без преобразования. Number(x) или parseInt(x, 10) —
и всегда с основанием 10.
Шпаргалка
sort()по умолчанию сортирует как строки:[1, 10, 100, 9].Всегда
===. Исключение —x == nullдля обеих пустот сразу.Пустой массив — истина. Проверяй
.length.+со строкой склеивает,-преобразует в числа.?.безопасный доступ,??значение по умолчанию (не путать с||).constзапрещает переприсваивание, но не изменение содержимого.