Каждый разработчик знает, как раздражает внезапная «магия» в JavaScript: числа превращаются в Infinity, строка «123px» вдруг становится 123, а NaN ведёт себя так, будто у него свои законы. И особенно больно, когда всё это нужно аккуратно встроить в приложение на C++/Qt, не потеряв контроль над логикой.
Эта глава раскроет, как устроены встроенные объекты JavaScript и почему профессиональные разработчики опираются на них, чтобы писать предсказуемые сценарии. Вы обнаружите неочевидные места, где «типизация по настроению» ломает расчёты, и узнаете секрет, как быстро проверять корректность данных до того, как они попадут в ваш Qt-код — экономя часы отладки и повышая надёжность.
Внутри — 6 ключевых семейств объектов: Global (и его расширение из Qt), Number (пределы, isFinite/isNaN), String (replace/substring/slice + includes/startsWith), RegExp (test/search/match/split и замена через функцию), Array (push/splice/sort/spread), Date и Math (точность, округления, sqrt, random).
Не дочитав эту главу, легко пропустить пару «маленьких» нюансов, которые потом превращаются в большие баги.
В этой главе вы найдёте готовые к использованию примеры кода.
Самопроверка по главе
Почему создание объекта Boolean не рекомендуется в JavaScript, хотя такой объект существует?Ответ
Правильный ответ: Объект Boolean всегда приводится к true (даже new Boolean(false)), что создаёт путаницу. Лучше использовать примитивные значения true/false, которые ведут себя предсказуемо.
Какие шесть значений в JavaScript преобразуются к false при логическом преобразовании?Ответ
Правильный ответ: Это 0, null, false, NaN, undefined и пустая строка (“”). Все остальные значения, включая пустые массивы и объекты, преобразуются к true.
Почему метод Number.isNaN(“hello”) возвращает false, хотя строка явно не является числом?Ответ
Правильный ответ: Number.isNaN() проверяет, является ли значение именно NaN, а не “не числом вообще”. Строка “hello” — это строка, а не значение NaN. Для проверки “не число” нужно использовать isNaN() без Number.
Что произойдёт, если в методе splice() указать второй параметр равным 0?Ответ
Правильный ответ: Элементы будут вставлены в указанную позицию без удаления существующих элементов, так как второй параметр определяет количество удаляемых элементов.
Почему в JavaScript месяцы в объекте Date нумеруются с 0, а дни месяца — с 1?Ответ
Правильный ответ: Это историческое наследие от языка Java, где месяцы представлены как индексы массива (0-11), а дни месяца — как календарные числа (1-31). Это создаёт путаницу, но сохранено для обратной совместимости.
Какое фундаментальное ограничение накладывает хранение дат в миллисекундах с 1 января 1970 года?Ответ
Правильный ответ: Использование дат до 1970 года становится проблематичным, хотя современные реализации поддерживают отрицательные значения для дат до этой точки отсчёта (эпохи Unix).
В чём разница между методами Math.ceil() и Math.floor() при работе с отрицательными числами?Ответ
Правильный ответ: ceil() округляет к большему значению (Math.ceil(-5.5) = -5), а floor() — к меньшему (Math.floor(-5.5) = -6). Для отрицательных чисел “больше” означает ближе к нулю.
Почему создание функций через new Function() выполняется медленнее, чем объявление обычных функций?Ответ
Правильный ответ: Потому что трансляция (компиляция) кода Function происходит при каждом его использовании в runtime, в то время как обычные функции компилируются один раз при загрузке скрипта.
Что вернёт выражение [“10”, “5”, “40”].sort() и почему результат может быть неожиданным?Ответ
Правильный ответ: Вернёт [“10”, “40”, “5”], потому что sort() по умолчанию сравнивает элементы как строки лексикографически. Для числовой сортировки нужна функция сравнения: sort((a,b) => a – b).
Как получить все совпадения регулярного выражения в строке с помощью метода match()?Ответ
Правильный ответ: Нужно использовать глобальный флаг g в регулярном выражении: str.match(/\d+/g). Без флага g метод вернёт только первое совпадение.
Почему метод push() часто предпочтительнее прямого присваивания по индексу для добавления элементов в массив?Ответ
Правильный ответ: push() автоматически добавляет элемент в конец, не требуя знания длины массива, и возвращает новую длину. Прямое присваивание может создать “дыры” в массиве, если индекс больше длины.
Что произойдёт, если передать в Math.sqrt() отрицательное число?Ответ
Правильный ответ: Метод вернёт NaN (Not a Number), так как квадратный корень из отрицательного числа в действительных числах не определён.
Как с помощью оператора spread (…) найти максимальное значение в массиве чисел?Ответ
Правильный ответ: Math.max(…arr) — оператор spread разворачивает массив в отдельные аргументы для метода max(), который принимает несколько параметров, а не массив.
Почему методы trim(), trimStart() и trimEnd() полезны при обработке пользовательского ввода?Ответ
Правильный ответ: Они удаляют случайные пробелы, которые пользователь мог добавить при вводе, что делает валидацию и сравнение данных более надёжными и устраняет проблемы с форматированием.
В чём преимущество метода replace() с функцией обратного вызова вместо простой строки замены?Ответ
Правильный ответ: Функция позволяет динамически вычислять замену для каждого совпадения, что даёт возможность трансформировать найденные значения (например, умножать числа, изменять регистр) вместо простой подстановки.
Практические задания
Простой уровень
Валидатор строк с фильтрацией
Создайте функцию, которая принимает массив строк и возвращает новый массив, содержащий только те строки, которые: (1) длиннее 3 символов, (2) не содержат цифр, (3) обрезаны от пробелов. Например, [” hello “, “hi”, “test123”, “world”] должен вернуть [“hello”, “world”].
Подсказки: Используйте методы filter(), trim(), length и регулярное выражение /\d/.test() для проверки наличия цифр. Комбинируйте условия в функции-предикате для filter().
Средний уровень
Анализатор временных рядов
Создайте функцию, которая принимает массив объектов Date и возвращает статистику: количество дат в каждом месяце года, самую раннюю и самую позднюю даты, а также среднее количество дней между последовательными датами. Учтите, что массив может быть не отсортирован.
Подсказки: Используйте getMonth() для группировки по месяцам, Math.min/max с spread оператором для поиска крайних дат, sort() для упорядочивания массива. Для вычисления разницы в днях между датами вычтите миллисекунды и разделите на (1000 * 60 * 60 * 24).
Сложный уровень
Умный парсер математических выражений
Создайте функцию-калькулятор, которая принимает строку с математическим выражением (например, “2 * (3 + sqrt(16)) / abs(-4)”) и вычисляет результат. Функция должна поддерживать: базовые операции (+, -, *, /), скобки, математические функции из Math (sqrt, abs, pow, sin, cos), и возвращать ошибку при некорректном синтаксисе.
Подсказки: Используйте RegExp для замены математических функций на их вызовы (replace(/sqrt\((\d+)\)/g, match => Math.sqrt(…))), очистите строку от пробелов с trim(), используйте eval() осторожно или создайте Function() для безопасного выполнения. Оберните в try-catch для обработки ошибок. Рассмотрите использование replace() с функцией для замены всех функций Math.
💬 Присоединяйтесь к обсуждению!
Удалось разобраться с тонкостями встроенных объектов JavaScript? Обнаружили неочевидное поведение Math или Date?
Поделитесь своими решениями практических заданий, обсудите лучшие практики работы с массивами и регулярными выражениями, или помогите другим читателям разобраться с хитростями языка!