Почему document.all ложно?

document.all это не примитивный объект в DOM, который является ложным.

Например, этот код ничего не делает:

if (document.all) {
    alert("hello");
}

Может кто-нибудь объяснить, почему это так?

6 ответов

Отказ от ответственности: я парень, который написал в Твиттере вопрос, который привел к этой теме:) Это был вопрос, который я хотел бы задать и ответить в моем выступлении Front-Trends. Я написал этот твит за 5 минут до выхода на сцену.


Вопрос, который я задавал, заключается в следующем.

Спецификация ECMAScript определяет ToBoolean() следующим образом:

ToBoolean (условие), слайд из моего выступления Front-Trends 2012

Как видите, все не примитивные объекты (т.е. все объекты, которые не являются логическими, число, строка, 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:

Итак, в конце концов, другие браузеры начали внедрять .

Однако, поскольку веб-сайты использовались для обнаружения 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/

Другие вопросы по тегам