Найти тему
Николай Беспалов

Устранение нетривиальной ошибки JavaScript.

Привет сегодня расскажу как можно с помощью отладчика решить, на мой взгляд, нетривиальную проблему JavaScript.

В JavaScript объекты это составной тип данных, его значение передается по ссылке. Другими словами, когда мы передаем объект в функцию как параметр или где угодно можем поменять его свойства. Используя инструкцию состоящую из выражения переменной, хранящей ссылку, а также операторов точка и присваивания. После этого другие инструкции, которые работают или будут работать с этой переменной/параметром, по ссылке получат изменение свойства.

Часто такое поведение искажает данные пользователя, приводит к ошибкам и является нежелательным.

Поиск источника таких нежелательных изменений свойств может занять долгое время: так программа может быть уже большой и состоять из сотни тысяч инструкций.

Давайте рассмотрим простой пример.

Ошибка получения свойства slice у undefined
Ошибка получения свойства slice у undefined

Сейчас веб программа не работает, так как в консоли есть ошибка и нет вывода ФИО.

Читаем самое верхнее сообщение в консоли

`Uncaught (in promise) TypeError: Cannot read property 'slice' of undefined`

Не обработано (в промис) ошибка типа: не могу прочитать свойство slice от неопределено.

Нажимаем на ссылку и переходим к месту ошибки.

-2

`getFullName() {

return `${this.lastName} ${this.firstName.slice(0, 1)}. ${this.middleName.slice(0, 1)}.`

}`

Видим, что ошибочное выражение ` this.firstName.slice(0, 1) ` состоит из четырех операторов:

  • два оператора точка
  • один оператор-разделитель запятая
  • один оператор группировки - пара круглых скобок

Давайте читать инструкцию. Первым вычисляется левое выражение `this.firstName` Оно состоит из оператора точка, слева первичное выражения this и идентификатора firstName справа. Результатом этого выражения будет undefined. Выполнение следующего оператора точка вызывает ошибку. Так как оператор точка работает только с объектными типами, его выполнение от undefined приводит к ошибке — не могу получить свойство slice от undefined.

Получается, что где-то этому свойству было присвоено значение undefined....

Чтобы решить эту проблему попробуем пойти от обратного. Воспользуемся инструментом отладки <b>остановка на исключении</b>. Двигаясь от места ошибки по стеку вызовов вниз, попробуем перейти к инструкции, которая изменила свойство.

Выбираем инструмент остановка на исключении

Видим что в стеке всего два вызова

-3

Переходим в предыдущий вызов.

-4

Видим, что нет явной инструкции, которая изменяют свойство firstName.

Делаем вывод, что изменение не происходит в этом стеке вызовов.

Как вам такое?

И как найти негодяя который изменил свойство моего объекта?

Пожалуйста, напишите в комментах как бы вы нашли его?

Ребята, кто со мной работает и кому я это рассказал, напишите плиз звездочку в комменте.

Мне очень интересно узнать, как другие специалисты JavaScript решают подобные проблемы.

Знаете, когда я встретился с этим поведением JavaScript впервые, я потратил пару часов на расследование и выдрал клок волос с челки....

Отключаем инструмент пауза на исключении.

Итак, вот наш новый план мы определим в объекте user свойство firstName, используя геттер и сеттер.

В сеттер добавим инструкцию отладки, используя оператор debugger и оператор точка с запятой.

`const user = {

_firstName: 'Vasilij',

set firstName(value) {

debugger;

this._firstName = value;

},

get firstName() {return this._firstName},

middleName: 'Alibabaevich',

lastName: 'Radner',

aka: 'Alibaba',

getFullName() {

return `${this.lastName} ${this.firstName.slice(0, 1)}. ${this.middleName.slice(0, 1)}.`;

}

};`

Двигаясь дальше по стеку вызовов, найдем инструкцию, которая меняет свойство firstName.

Отладчик остановился в сеттере до того, как новое значение будет записано в объект.

-5

Видим, что значение параметра value undefined.

Теперь, используя стек вызовов мы легко переходим в предыдущий вызов.

-6

иииии, победааааа ура.

Есть еще более простой способ решить эту проблему, используя инструмент отладки остановка на исключении.

Вот наш новый план: сделаем объект user не объектом и, используя инструмент остановку по исключению, легко попадем к ошибочной инструкции.

Так как мы знаем, что при попытке получения свойства от undefined возникает ошибка.

Включаем инструмент остановка на исключении, присваиваем переменной user значение undefined.

` const user = undefined;`

-7

Мы снова остановились в месте искажения свойства firstName.

-8

Это все, что я хотел вам сегодня рассказать об отладке нежелательных изменений у объектов.

Спасибо за прочтение статьи. Ставьте лайки, подписывайтесь на канал, делитесь этим видео и статьей с друзьями, всего хорошего.

https://github.com/NVBespalov/js-lessons/tree/error/property-mutation