Разбор ошибки на самом простом примере из практического пособия по изучению программирования на JavaScript.
При работе с JavaScript можно столкнуться с такой ошибкой:
Связно это с тем, что JavaScript работает с содержимым и структурой документа (DOM). И если содержимое не обозначено, то будет возникать ошибка. Под содержимым в даном случае подразумевается html теги.
Например, в этом коде:
myHeading.textContent = 'Hello world!';
Используется отсылка к структуре h1. Если тега h1 в документе нет, то будет возникать соответствующая ошибка:
TypeError: null is not an object
Также ошибка будет возникать и в том случае, если код JavaScript будет загружаться перед H1, а не после. Это опять же исходит из того, что JavaScript воздействует на уже загруженные элементами. А если они загружаются после кода, то влиять на них он не может, потому что ещё не «видит».
Таким образом, если возникает ошибка TypeError: null is not an object смотрите какого объекта в структуре документа нет. Возможно перед JavaScript-ом не хватает простых div:
Идея: низкоуровневые ошибки! Почему в Javascript нет низкоуровневых ошибок (Warning/Notice), как в PHP? Можно просто выбрасывать предупреждение: Warning: null is not an object или Warning: Cannot set property «…» of null
Метод document.querySelector(); не должен тихо возвращать null. Можно выбрасывать замечание Notice: DOM element «selector» not found и после него возвращать null.
Кстати, низкоуровневые ошибки нужны в куче случаев, которые приводят к логическим ошибкам.
Пример: некорректные математические операции.
3 * «string»;
должно выбросить предупреждение или замечание:
A non-numeric value encountered
а уже потом вернуть NaN. Должен быть предусмотрен отдельный оператор конкатенации строк, использование «+» должно приводить к низкоуровневой ошибке.
Другие примеры.
Деление на нуль должно генерировать замечание Notice: Division by zero
Попытка обратиться к undefined или null, а также к булеву значению как к объекту должно генерировать предупреждение и возвращать null или undefined при попытке чтения свойства.
Использование неитерируемого объекта в цикле for…of должно выбрасывать предупреждение вместо исключения.
Методы, работающие с DOM, должны выбрасывать предупреждения вместо DOMException.
Вывод напрямую массива или объекта в document должно сгенерировать замечания или предупреждения:
Array to string conversion
Object to string conversion
Должна быть предусмотрена низкоуровневая ошибка Deprecated.
И это далеко не все случаи, учитывая, что ECMAScript продолжает развиваться.
Нужна функция типа PHP функции trigger_error(), выбрасывающая низкоуровневые ошибки, нужны предопределенные константы E_NOTICE, E_WARNING и E_DEPRECATED, а также константы E_CONSOLE и E_DOCUMENT, определяющие, куда выводить сообщение или предупреждение.
Например: window.triggerError(«Caught error on strong», E_WARNING, E_CONSOLE);
Не будут лишними и синтаксические предупреждения (предупреждения парсинга), например, пропуск точки с запятой, использование зарезервированного слова или код после return.
PHP может работать, если ошибки не fatal. JavaScript устроен иначе. Почти любая ошибка сломает дальнейший код. Так исторически сложилось, не думаю, что это сегодня можно изменить.
Что касается:
есть TypeScript и надстройки над JavaScript, которые делают строгую типизацию в JS.
В приведенных примерах бросается исключение. Можно просто не бросать исключение, а писать в консоль или в HTML-код страницы предупреждение и выполнять какое-то действие по умолчанию. При этом обработчик ошибок не будет считать сообщение ошибкой. Например, код:
console.log(obj.data.data);
console.log(obj.baz.data);
должен вывести в консоль null и undefined и два предупреждения:
В PHP тоже постепенно заменяют Fatal error на исключения, которые сами по себе, если не отловить, Fatal error.
В принципе, несложно написать код на языке C++ (или на каком написан движок для браузеров?), реализующий помимо исключения низкоуровневую ошибку, не трогая остальной код движка. Сами исключения никуда не денутся. Кстати, как устроен Javascript, в двух словах? Просто замена предлагается постепенная. Слишком уж странная логика ошибок у Javascript. Деление на ноль, некорректная математическая операция, вывод неопределенных значений в HTML, обращение к булеву значению как к объекту, вызов функции без аргументов не вызывает даже вывода предупреждающего сообщения, не говоря уж об ошибках.
Система ошибок Javascript подчиняется каким-то стандартам (IEEE и т.д.)?
TypeScript не рассматриваем, так как он пока не встроен в Javascript. Предлагаемые изменения нужно именно встроить.
Кстати, в Chrome нашел баг: код
не вызовет ошибки, а создаст DOM элемент null, разумеется, невалидный.
Изучил исходный код движка V8 на C++ — тёмный-тёмный лес. Совершенно непонятно, где происходит выбрасывание ошибки типа «x is not defined» или «cannot read property of null». Поиск мало чего дает. Не нашёл исходного кода методов querySelector и других для работы с DOM. Не найдено исходного кода методов встроенных объектов. Где все это реализовано?
Изучил исходный код Chromium на Github и нашел такой код C++
void некая_функция_уведомления(message) {
сообщить_в_консоли("Notice: " + message);
}
void некая_функция_предупреждения(message) {
предупредить_в_консоли("Warning: " + message);
вывести_в_HTML("<br /><b>Warning</b>: " + message + "<br />");
}
*/
// ...
if (selectors.IsEmpty()) {
// вместо этого кода
exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
"The provided selector is empty.");
// написать это:
/*
некая_функция_предупреждения("The provided selector is empty.");
*/
return nullptr;
}
// ...
CSSSelectorList selector_list = CSSParser::ParseSelector(
MakeGarbageCollected<CSSParserContext>(
document, document.BaseURL(), true /* origin_clean */,
document.GetReferrerPolicy(), WTF::TextEncoding(),
CSSParserContext::kSnapshotProfile),
nullptr, selectors);
if (!selector_list.First()) {
// сюда вместо этого кода написать вывод предупреждения
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"'" + selectors + "' is not a valid selector.");
/*
некая_функция_предупреждения("'" + selectors + "' is not a valid selector.");
*/
return nullptr;
}
И еще, если я правильно понял, код, где возвращается элемент или null:
QUERY_STATS_RESET();
if (selectors_.IsEmpty())
return nullptr;
if (needs_updated_distribution_)
target_element.UpdateDistributionForFlatTreeTraversal();
for (Element* current_element = &target_element; current_element;
current_element = current_element->parentElement()) {
if (SelectorListMatches(target_element, *current_element))
return current_element;
}
// Здесь должно быть уведомление
/*
некая_функция_уведомления("DOM element '" + selectors + "' not found.");
*/
return nullptr;
}
Думаю, что хоть вы в C++ и не разбираетесь особо, понятно. Кстати, раз уж пошла такая тема, то напишете статью про распространенные ошибки в Javascript с объяснениями? Заранее спасибо.
Предлагаемый список низкоуровневых ошибок в Javascript:
Вместо ReferenceError: x is not defined:
Notice: x is not defined in in %s on line %d и возврат undefined.
Использование неопределенного индекса массива (вывод в document, передача в функцию):
Notice: Undefined index: x in in %s on line %d.
Использование переменной или свойства с неопределенным значением (тоже вывод в document, передача в функцию): Notice: Undefined variable or property: x in in %s on line %d.
Вместо TypeError: Cannot read property of null/undefined: Warning: Cannot read/set property of null/undefined/boolean in in %s on line %d и возврат null или undefined.
Вместо DOMException, появляющихся при обработке DOM объектов — Warning и Notice.
Некорректные математические операции (строка, умноженная на число и т.п.) — Notice: A non well formed numeric value encountered in in %s on line %d и Notice: A non-numeric value encountered in in %s on line %d.
Деление на ноль — Notice: Division by zero in in %s on line %d.
Рассмотрим ES6:
В цикле for…of вместо исключения — Warning: x is not iterable in in %s on line %d.
При пропуске или избытке аргументов функций
Warning: Function ‘fun’ expects * parameters, * present in in %s on line %d
При неправильном использовании BigInt — Warning.
И это не всё!
Также нужны ошибки Deprecated.
Кстати, необязательно использовать notice, достаточно трех типов ошибок: исключение, предупреждение и сообщение об устаревшем коде.