Почему document.all ложно?
document.all
это не примитивный объект в DOM, который является ложным.
Например, этот код ничего не делает:
if (document.all) {
alert("hello");
}
Может кто-нибудь объяснить, почему это так?
6 ответов
Отказ от ответственности: я парень, который написал в Твиттере вопрос, который привел к этой теме:) Это был вопрос, который я хотел бы задать и ответить в моем выступлении Front-Trends. Я написал этот твит за 5 минут до выхода на сцену.
Вопрос, который я задавал, заключается в следующем.
Спецификация ECMAScript определяет ToBoolean()
следующим образом:
Как видите, все не примитивные объекты (т.е. все объекты, которые не являются логическими, число, строка, undefined
, или же null
) правдивы согласно спецификации. Однако в DOM есть одно исключение - ложный объект DOM. Вы знаете, кто это?
Ответ document.all
, Спецификация HTML гласит:
all
атрибут должен возвращатьHTMLAllCollection
коренится вDocument
узел, чей фильтр соответствует всем элементам.Объект, возвращаемый для всех, имеет несколько необычное поведение:
Пользовательский агент должен действовать так, как если бы
ToBoolean()
Оператор в JavaScript преобразует объект, возвращенный дляall
кfalse
значение.Пользовательский агент должен действовать так, как будто для целей
==
а также!=
операторы в JavaScript, объект возвращен дляall
равноundefined
значение.Пользовательский агент должен действовать так, чтобы
typeof
Оператор в JavaScript возвращает строку'undefined'
применительно к объекту, возвращенному дляall
,Эти требования являются преднамеренным нарушением спецификации JavaScript, действующей на момент написания (редакция ECMAScript 5). Спецификация JavaScript требует, чтобы
ToBoolean()
оператор преобразует все объекты вtrue
стоимость, и не имеет положений для объектов, действующих, как если бы они былиundefined
для целей определенных операторов. Это нарушение вызвано желанием совместимости с двумя классами устаревшего контента: тот, который использует наличиеdocument.all
как способ обнаружить устаревшие пользовательские агенты, и тот, который поддерживает только эти устаревшие пользовательские агенты и используетdocument.all
объект без проверки на его наличие в первую очередь.
Так, document.all
является единственным официальным исключением из этого правила ECMAScript. (В опере document.attachEvent
и т.д. тоже ложные, но это нигде не видно.)
Приведенный выше текст объясняет, почему это было сделано. Но вот пример фрагмента кода, который очень распространен на старых веб-страницах, и который проиллюстрирует это далее:
if (document.all) {
// code that uses `document.all`, for ancient browsers
} else if (document.getElementById) {
// code that uses `document.getElementById`, for “modern” browsers
}
В основном, надолго document.all
был использован таким образом, чтобы обнаружить старые браузеры. Так как document.all
Сначала проверяется, однако, более современные браузеры, которые предлагают оба свойства, все равно окажутся в document.all
Путь к коду. В современных браузерах мы бы предпочли использовать document.getElementById
, конечно, но так как большинство браузеров все еще имеют document.all
(по другим причинам обратной совместимости) else
никогда не будет доступен, если document.all
было правдой. Если бы код был написан по-другому, это не было бы проблемой:
if (document.getElementById) {
// code that uses `document.getElementById`, for “modern” browsers
} else if (document.all) {
// code that uses `document.all`, for ancient browsers
}
Но, к сожалению, большая часть существующего кода делает это наоборот.
Самое простое решение этой проблемы - просто document.all
быть ложным в браузерах, которые все еще имитируют это.
ES2019 Обновление
Теперь есть внутренний слот [[IsHTMLDDA]] для объектов:
Внутренний слот [[IsHTMLDDA]] может существовать для объектов, определенных реализацией. Объекты с внутренним слотом [[IsHTMLDDA]] ведут себя как
undefined
в абстрактных операциях ToBoolean и Abstract Equality Comparison и при использовании в качестве операнда дляtypeof
оператор.
Стандарт HTML также был обновлен, чтобы добавить этот внутренний слот для объектов, реализующих HTMLAllCollection
интерфейс:
Объекты, реализующие интерфейс HTMLAllCollection, являются объектами устаревшей платформы с дополнительным внутренним методом [[Call]], описанным в разделе ниже. У них также есть внутренний слот [[IsHTMLDDA]].
Причина этого безумия указана в этой заметке в стандарте HTML:
Такое особое поведение мотивировано стремлением к совместимости с двумя классами унаследованного контента: один, который использует наличие
document.all
как способ обнаружения устаревших пользовательских агентов, и тот, который поддерживает только эти устаревшие пользовательские агенты и используетdocument.all
объект без предварительной проверки на его наличие.
Таким образом, стандарт хочет быть совместим с этими двумя типами кода:
Код, который проверяет, работает ли он в Internet Explorer, чтобы использовать его нестандартные функции, например
document.all
и Activex;if (document.all) { useActiveXStuff(); }
Код, который предполагает, что он работает в Internet Explorer и использует
document.all
.document.all["my-button"].onclick = function () { alert("hi"); };
Короче говоря, это заставляет ОБА из этих примеров кода работать. Браузеры должны сделать это, чтобы старые веб-страницы продолжали работать.
Образец 1
// Internet Explorer
if (document.all) {
useActiveX()
}
// Netscape Navigator
else {
useOldButStillWorkingCode()
}
Образец 2
document.all.output.innerHTML = 'Hello, world!'
Современные браузеры больше не реализуют эту устаревшую вещь. Это было введено IE, но большинство других "подкладывают" это, чтобы быть совместимым.
Чтобы сделать возможным обнаружение браузера (в прежние времена вы могли отличить IE от NN, протестировав document.all
), поддерживая синтаксис document.all, другие браузеры сделали "странную" реализацию, которая typeof document.all
возвращает неопределенное.
Opera> document.all
// prints the array-like object
Opera> typeof document.all
"undefined"
Opera> Boolean(document.all)
false
Прежде чем FF прекратил поддержку, он также показал странное поведение, как указано в этом сообщении. Вы можете найти больше внутренностей в Mozilla bug # 412247.
В архиве списка рассылки W3C также есть очень длинная тема, начиная с http://lists.w3.org/Archives/Public/public-html/2009Jun/0546.html
Другие ответы уже дают хорошее объяснение того, почему ведет себя именно так.
Однако мне было очень любопытно узнать больше об исторической перспективе. Откуда взялось и почему современные браузеры поддерживают его по сей день?
Итак, я провел небольшое исследование, чтобы узнать об этом больше, и вот что я нашел.
Первоначально был представлен в Internet Explorer 4. Его основным назначением был доступ к элементам по идентификатору, например:
var element = document.all[id]
Позже W3C стандартизировалdocument.getElementById
как способ получить элементы по их идентификатору.
Однако, поскольку IE в течение многих лет занимал самую большую долю рынка, многие веб-сайты продолжали использовать его, не проверяя его.
Некоторые из этих веб-сайтов были довольно популярны и работали в других браузерах, кроме IE.
Поэтому начались дискуссии о добавлении поддержки в других браузерах, чтобы использование веб-сайтов работало в этих браузерах.
Чтобы дать вам несколько примеров того, что обсуждалось, вот два обсуждения от bugzilla:
- https://bugzilla.mozilla.org/show_bug.cgi?id=74201
- https://bugzilla.mozilla.org/show_bug.cgi?id=154589
Итак, в конце концов, другие браузеры начали внедрять .
Однако, поскольку веб-сайты использовались для обнаружения IE с помощьюif
высказывания типа этого:
if (document.all) {
// Use proprietary Internet Explorer APIs
}
Чтобы другие браузеры не были ошибочно определены как Internet Explorer, W3C стандартизировал их как ложный объект, который ведет себя как неопределенный.
Таким образом, по состоянию на 2023 год он по-прежнему поддерживается во всех основных браузерах. Почему вы можете спросить? Вероятно, потому, что они хотят, чтобы старые сайты работали.
На самом деле я сделал двухминутное видео на YouTube оdocument.all
и его историю, так что, если вам это интересно, посмотрите: https://youtu.be/KFasyUpmoss
document.all не единственный объект, который является ложным. Другой вопрос был опубликован по этому поводу, и в качестве примера скрипки в ответе показано, что в документе много ложных объектов. Количество варьируется в зависимости от используемого браузера.
Посмотрите этот вопрос. Все объекты в JavaScript соответствуют действительности, но в DOM нет одного непримитивного объекта. Который?
И скрипка, которая отображает все ложные объекты документа http://jsfiddle.net/UTNkW/