При рассмотрении проблемы с Microsoft Edge и Facebook SDK несколько недель назад мы столкнулись с очень распространенной ошибкой: Cannot read property 'length' of undefined. На самом деле, это 6-я по распространенности ошибка, затрагивающая почти 28% аккаунтов.

Эта ошибка указывает на то, что наш код ожидает наличие объекта со свойством length, но этот объект отсутствует. length обычно используется для string и array, но пользовательский объект также может иметь это свойство.

Это блокирующая ошибка, и выполнение будет остановлено. Важно защищать тип данных, передаваемых из внешних скриптов и систем, прежде чем взаимодействовать с ними.

Основная причина

Эта ошибка может возникать по многим причинам, так как очень часто во время повседневной разработки ссылаются на свойство length для string или array.

В нашем случае мы оборачивали функцию JavaScript для перехвата fetch запросов на инструментирование, и мы ожидали, что свойство url, как определено в спецификации выборки, будет строкой.

Facebook SDK не вызывал fetch со строкой. Они назвали это сериализуемым объектом. 🤦‍♂️.

Они создали объект, похожий на класс URL-адреса nodejs, который обеспечивает более простой доступ и утилиты для создания строки URL-адреса. Пользовательский объект может быть сериализован как URL-адрес с реализацией пользовательской функции toString. Вот упрощенный пример:

fetch({ 
  protocol: “https”, 
  hostname: “example.com”, 
  path: “/foo”, 
  toString: function() { 
    return `${this.protocol}://${this.hostname}${this.path}`; 
  } 
});

Передача сериализуемого объекта в fetch не входит в спецификацию, однако похоже, что она работает во всех современных браузерах.

Из-за особенностей нашей упаковки fetch мы предположили, что url имеет другой тип, и выдали ошибку, пытаясь получить длину чего-то, что не было строкой.

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

Как исправить

Всегда тщательно проверяйте данные из внешних источников, включая запросы API, сетевые ответы и вызовы функций из других скриптов. Если вы принимаете значение string, оно может принимать несколько разных форм:

  • Строковый литерал "like this"
  • Строковый объект new String("like this")
  • Сериализуемый объект { toString: () => "like this" }

Конечно, это также может быть null, undefined или совершенно другой неподдерживаемый тип. В большинстве случаев мы можем просто привести значение к строке, что гарантирует работу наших строковых функций.

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

function myFunc(unknownStringVar stringy) { 
  var knownStringVar = “” + stringy; 
  // go about your day 
}

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

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

Первоначально опубликовано на https://trackjs.com.