Сталкивались ли вы с ощущением “я же знаю C++”, а потом открываете JavaScript в Qt — и внезапно ловите странные баги из-за undefined, приоритетов операторов и “магии” неявных преобразований? Каждый разработчик знает, как раздражает, когда код выглядит знакомо, но ведёт себя иначе.
Эта глава раскроет, почему синтаксическое сходство с C++ — это ловушка, и вы обнаружите неочевидные правила, которые профессиональные разработчики используют, чтобы сценарии в Qt работали предсказуемо. Здесь узнаете секрет, как писать JavaScript-код так, чтобы он не превращался в “язык ада”, а ускорял разработку и снижал количество ошибок.
Сравнение == против ===, три способа объявления переменных (var/let/const) и “подводные камни” typeof и null/undefined. Плюс — разбор приоритетов операторов на примере, где один символ меняет результат, и практические конструкции try/catch для устойчивого кода.
Если эти нюансы пропустить сейчас — они догонят на реальном проекте. Лучше закрыть пробел, пока он не стал баг-репортом.
Самопроверка по главе
Почему в Qt6 рекомендуется использовать let и const вместо var для объявления переменных?Ответ
Правильный ответ: Ключевое слово var обеспечивает функциональную область видимости, что может привести к неожиданным эффектам. Let и const предоставляют блочную область видимости, делая код более предсказуемым и безопасным. Использование var считается устаревшим подходом.
В чём ключевая разница между операторами == и ===, и почему это важно при сравнении?Ответ
Правильный ответ: Оператор == выполняет преобразование типов перед сравнением (например, true == 1 вернёт true), а === проверяет как значение, так и тип без преобразования (true === 1 вернёт false). Это критично для предотвращения неожиданных результатов сравнения.
Что произойдёт с результатом выражения let i = 5; const a = i++; и почему значения будут отличаться от ++i?Ответ
Правильный ответ: Переменная a получит значение 5, а i станет равна 6. Постфиксная форма (i++) сначала возвращает текущее значение, затем увеличивает переменную. Префиксная форма (++i) сначала увеличивает, потом возвращает результат.
Почему операторы инкремента и декремента имеют самый высокий приоритет среди арифметических операций?Ответ
Правильный ответ: Высокий приоритет обеспечивает предсказуемое изменение переменной до участия в других операциях. Это критично для правильного вычисления сложных выражений, где инкремент должен выполниться раньше умножения, деления или сложения.
Как JavaScript обрабатывает сложение числа со строкой, например 7 + ” is a number”?Ответ
Правильный ответ: JavaScript автоматически преобразует число к строковому типу и выполняет конкатенацию строк. Результатом будет строка “7 is a number”. Это пример неявного преобразования типов в слабо типизированном языке.
Зачем в функции конструктора использовать this.call() при наследовании класса?Ответ
Правильный ответ: Метод call() позволяет вызвать конструктор родительского класса в контексте нового объекта (через this), инициализируя унаследованные свойства. Без этого наследуемые атрибуты не будут корректно установлены в дочернем классе.
Почему использование оператора with не рекомендуется в современном JavaScript?Ответ
Правильный ответ: Оператор with создаёт неоднозначность в области видимости переменных, затрудняет оптимизацию кода и считается устаревшим. В строгом режиме “use strict” его использование вызывает ошибку. Вместо него следует использовать деструктуризацию.
В чём принципиальная разница между null и undefined?Ответ
Правильный ответ: Значение undefined означает, что переменная объявлена, но не инициализирована. Значение null — это явное указание на отсутствие значения, присваиваемое программистом. Важно: typeof null возвращает “object” из-за исторического бага JavaScript.
Как создать приватные члены класса в JavaScript без ключевого слова private?Ответ
Правильный ответ: Приватные члены создаются через замыкание — объявлением переменных и функций с помощью let внутри конструктора. Они будут доступны только методам, определённым в конструкторе, но недоступны извне объекта.
Что произойдёт, если внутри функции использовать переменную без объявления через let/const/var?Ответ
Правильный ответ: Переменная станет глобальной и будет доступна за пределами функции. Это не рекомендуется, так как может привести к конфликтам имён. В строгом режиме “use strict” это вызовет ошибку.
Почему JSON предпочтительнее XML для обмена данными в JavaScript-приложениях?Ответ
Правильный ответ: JSON-строки являются валидным исходным кодом JavaScript и могут быть выполнены через eval() без преобразования. JSON более компактен, читабелен и нативно интегрирован с JavaScript, что делает его удобнее XML.
Как использовать prototype для расширения возможностей стандартных объектов JavaScript?Ответ
Правильный ответ: Через prototype можно добавлять новые методы к существующим классам, например: Date.prototype.printFullYear = function() {…}. Все экземпляры класса автоматически получат доступ к новому методу без изменения исходного кода класса.
Зачем нужна секция finally в блоке try-catch, если код можно написать после всей конструкции?Ответ
Правильный ответ: Блок finally гарантирует выполнение кода независимо от того, произошла ошибка или нет, даже если в catch используется return или throw. Код после try-catch может не выполниться при досрочном выходе из функции.
Практические задания
Простой уровень
Калькулятор с проверкой типов
Создайте функцию calculate(), которая принимает два параметра и операцию (строку: “+”, “-“, “*”, “/”). Функция должна проверять типы входных данных с помощью typeof и строгого сравнения (===), и возвращать результат операции или сообщение об ошибке, если типы не подходят. Используйте let для переменных и const для константных значений.
Подсказки: Используйте оператор typeof() для проверки типов. Примените строгое сравнение (===) для проверки операции. Обработайте случай деления на ноль. Используйте switch для выбора операции.
Средний уровень
Класс Point с приватными членами
Реализуйте класс Point2D с приватными координатами x и y, используя замыкания. Добавьте методы для получения координат, их установки, вычисления расстояния до другой точки, и статический метод для вычисления расстояния между двумя точками. Расширьте класс до Point3D через наследование, добавив координату z. Используйте prototype для добавления метода toString() ко всем точкам.
Подсказки: Используйте let внутри конструктора для создания приватных переменных. Примените call() для вызова родительского конструктора. Формула расстояния: Math.sqrt((x2-x1)² + (y2-y1)²). Для статического метода добавьте свойство напрямую к функции конструктора.
Сложный уровень
Система управления задачами с JSON
Создайте систему управления задачами (Task Manager), которая хранит задачи в формате JSON. Реализуйте класс Task с приоритетом, статусом и сроком. Создайте класс TaskManager со следующими методами: добавление задачи, удаление по ID, фильтрация по статусу/приоритету, экспорт в JSON-строку, импорт из JSON. Используйте try-catch для обработки ошибок при парсинге JSON. Добавьте метод для расширения всех задач новым полем через prototype.
Подсказки: Используйте JSON.stringify() для экспорта и JSON.parse() для импорта. Примените Array.filter() для фильтрации задач. Оберните JSON.parse() в try-catch для обработки некорректных данных. Используйте hasOwnProperty() для проверки наличия полей. Создайте буквальный объект для хранения задач с методами для работы с ними.
💬 Присоединяйтесь к обсуждению!
Разобрались с тонкостями JavaScript в Qt6? Хотите узнать больше о прототипном наследовании или оптимальных практиках использования let/const?
Возникли вопросы о переходе с QtScript на QJSEngine? Поделитесь своим опытом работы с JSON или обсудите преимущества строгого режима “use strict”!