Freigeben über


Chakra. Функциональная совместимость означает нечто большее, чем просто стандарты

Как принимается решение о реализации функции, которая не входит в спецификацию стандартов? Как и всем поставщикам браузеров, нам часто приходится принимать такое решение. В этой публикации будет приведен ряд реальных примеров JavaScript для иллюстрации некоторых принципов, используемых для создания функционально совместимого браузера, когда спецификации стандартов становится недостаточно.

В идеале спецификация стандартов самодостаточна. Реализация JavaScript просто включала бы все, что указано в спецификации ECMAScript, и ничего более. Мы считаем, что спецификации, публикуемые организациями по стандартизации, такими как Ecma International или W3C, совершенно необходимы для обеспечения функциональной совместимости браузеров. Такие спецификации информируют исполнителей браузеров о том, какие функции они должны реализовать, а разработчиков веб-сайтов — какие функции они должны будут использовать.

В реальном мире Интернета все не так четко определено. Спецификации редко бывают совершенны, а порой их намеренно оставляют неполными или неоднозначными. Собственный опыт как редактора спецификации ES5 позволяет утверждать, что всегда имеются проблемы, не нашедшие окончательного решения. В результате широко внедряются и применяются функции, не определенные ни в одной спецификации стандартов. Разработчику, пытающемуся построить браузер, который работал бы в существующем Интернете, придется реализовать много функций, не предусмотренных спецификацией стандартов.

Что такое реальная грамматика регулярных выражений?

При создании Chakra тщательно соблюдалась спецификация ECMAScript 5 (ES5), включая грамматику регулярных выражений. Но когда началось тестирование на реальных веб-сайтах, стали попадаться страницы, которые не работали из-за синтаксических ошибок в некоторых литералах регулярных выражений. Например, сбой происходил на регулярных выражениях, содержащих такую правую квадратную скобку:

 next = /]/.exec(buffer);

Причина состояла в том, что грамматика регулярных выражений в стандарте ECMAScript включает ] в список символов, которые нельзя напрямую использовать как символ сопоставления. В спецификации сказано, что символ /]/ недопустим, вместо него следует применять /\]/. Оказывается, что подобное ограничение символа [ имеет важное значение, поскольку в противном случае будет невозможно анализировать такие литералы, как /axyz[a-d]qwer/. Вместе с тем, одинарная скобка ], которой не предшествует символ [, на самом деле не представляет препятствия для анализа. Однако стандарт все равно утверждает, что она недопустима.

На практике все браузеры принимают такие регулярные выражения, как /]/, а разработчики веб-сайтов пишут их.

Консенсусная функция — это термин, который мы используем для описания таких функций, которые не входят ни в один стандарт, но повсеместно реализуются способом, обеспечивающим взаимодействие. В общем, IE9 и любая другая реализация JavaScript должны включать такие консенсусные функции, чтобы работать с реальным веб-контентом. Настоящая сложность состоит в их выявлении, поскольку они не включены в спецификации стандартов.

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

Почему в IE9 нет методов с символами _ _ в именах?

ES5 включает поддержку методов считывания и задания значений. Аналогичные функции доступны в некоторых веб-браузерах уже много лет. В течение этого периода разработчики браузеров экспериментировали с различными синтаксисами и программными интерфейсами (API) для определения таких методов. До появления ES5 в наиболее широко реализованном API для определения методов считывания/задания применялись два метода с именами __defineGetter__ и __defineSetter__. Использование символов подчеркивания по обе стороны имени метода — условное обозначение, применяемое некоторыми разработчиками браузеров, чтобы пометить методы, которые являются экспериментальными функциями или обращаются к уникальным возможностям низкого уровня конкретной реализации JavaScript.

Члены комитета ECMA TC39 пришли к соглашению, что методы считывания и задания значений должны быть включены в спецификацию ES5, так как их польза очевидна. Вместе с тем, комитет TC39 принял решение создать новый API для их определения и воздержаться от стандартизации интерфейсов __defineXXX__.

Почему TC39 поступил именно так? Из-за существенных различий в поведении браузеров, предоставивших этот API. Стандартизация на основе общей семантики для этих методов будет означать, что некоторые или, возможно, все существующие реализации браузеров придется изменить, чтобы привести в соответствие с новой семантикой. А это, вероятно, сделает нефункциональными некоторые приложения, написанные для работы с конкретными браузерами.

Другая причина — стремление поддержать логическую цельность соглашений об именовании. Ни одно другое внедренное имя в ECMAScript не начинается с символа подчеркивания и не заканчивается им. Связь таких имен с экспериментальными функциями или реализацией специфической функции широко известно. Использование этого условного обозначения для стандартизированной функции будет вводить в заблуждение и снизит его полезность для будущих экспериментов.

Комитет TC39 разработал новый API на основе метода Object.defineProperty. Этот интерфейс поддерживает не только определение свойств методов считывания и задания, но также другие новые возможности ES5. Методы __defineXXX__ не были включены в спецификацию ES5.

Браузеры, в которых уже реализован API __defineXXX__, могут поддерживать его и впредь без изменений в целях обеспечения совместимости. Однако в новом коде, написанном для обеспечения совместимости браузеров, поддерживающих стандарт ES5, для определения свойств методов считывания и задания значений должен использоваться метод Object.defineProperty.

К группе разработчиков по-прежнему обращаются с пожеланиями добавить в IE9 программные интерфейсы __defineXXX__. Понятно почему. У некоторых разработчиков уже имеется код, в котором используются эти интерфейсы, и им хотелось бы, чтобы этот код выполнялся в IE9. Однако разработчики IE9 считают, что поддержка этих интерфейсов не будет отвечать коренным интересам обеспечения функциональной совместимости в Интернете. TC39 уже рассмотрел этот API и сделал вывод о нежелательности его стандартизации. Если в IE9 была бы добавлена поддержка интерфейсов __defineXXX__, этот API еще более приблизился бы к тому, чтобы стать постоянной консенсусной функцией в Интернете; это по существу отменило бы решение TC39.

К сожалению, в краткосрочной перспективе это создает небольшой объем дополнительной работы для разработчиков, в настоящее время использующих этот устаревший API. Однако создание для имеющегося кода простой библиотеки совместимости, которая реализовывала бы интерфейсы __defineXXX__ посредством метода Object.defineProperty представляется сущим пустяком.

Неработоспособная консенсусная функция

В коде JavaScript можно установить ссылку на функцию, а также ее можно вызвать в коде, предшествующем объявлению функции как таковому. Это возможно, потому что JavaScript логически «поднимает» все объявления функций в верхнюю часть вмещающего тела кода. JavaScript также допускает существование нескольких объявлений функции с одним именем. Рассмотрим простую функцию тестирования.

После вызова функция создала последовательность оповещений: “second f1”, “second f1”,”second f1”. Причина состоит в том, что JavaScript фактически обрабатывает эту функцию, как будто она была написана следующим образом:

 function testFuncDeclarationOrder () {
    function f1() {alert("first f1")}
    function f1() {alert("second f1")}
    f1();
    f1();
    f1();
 }

Спецификация ECMAScript исторически накладывала только одно ограничение на размещение объявления функции. Стандарт ECMAScript не включает в себя возможность помещать объявление функции внутри тела инструкции управляющей структуры. Пример кода приведен ниже.

 if (someCondition) {
    function f1() {alert("f1")}
 }

При анализе, в соответствии со спецификацией ECMAScript, приведенный выше код вызовет синтаксическую ошибку. Тем не менее, если запустить его в любом браузере, обнаружится, что ошибка не возникнет. Поддержка объявлений функций в любом месте, где разрешено размещение инструкции, является консенсусной функцией в Интернете. А если так, то почему это не включено в ES5? В разделе 12 спецификации ES5 фактически кое-что говорится по этому поводу.

ПРИМЕЧАНИЕ. Известно, что несколько широко используемых реализаций ECMAScript поддерживают использование объявления функции как инструкции. Вместе с тем, между реализациями имеются существенные и непримиримые вариации в семантике, применяемой к таким объявлениям функций. Ввиду этих непримиримых различий использование объявления функции как инструкции приводит к тому, что код не является надежно переносимым между реализациями. Рекомендуется в реализациях ECMAScript запретить такое применение объявления функции либо сопровождать объявление обязательным предупреждением. В будущих редакциях ECMAScript могут быть определены альтернативные переносимые средства для объявления функций в контексте инструкции.

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

Что же делает IE9? Запрещает ли IE9 такие объявления функций на уровне инструкций, как рекомендовано в спецификации ES5? Нет, в IE9 подобные объявления обрабатываются точно так же, как в предыдущих версиях IE.

Почему было принято такое решение? Потому что прежние эксперименты разработчиков браузеров продемонстрировали, что отвержение таких объявлений приводило к неработоспособности многих существующих веб-страниц.

Это может показаться удивительным. Каким образом веб-страница может зависеть от функций, поведение которых различно в разных браузерах и, тем не менее, быть совместимой с различными браузерами? Один из возможных ответов: на самом деле код не используется. Это может быть функция, которую никогда не вызывают, либо результат которой не является важным для функционирования страницы. С другой стороны, если реализация JavaScript обработала бы эти функции как синтаксические ошибки, то весь сценарий был бы отклонен, даже если функция никогда не вызывается.

Реализация столь опасной для разработчиков функции нежелательна. Однако ее существование в Интернете закреплено, так что выбора фактически нет.

А как обстоит дело с const?

Порой задается вопрос, будет ли в IE9 реализована поддержка объявления const. const — это нестандартное расширение JavaScript, предоставляющее способ объявления значений «именованных постоянных». Типичное применение может выглядеть примерно так:

const pi=3.14159;

Несколько браузеров поддерживают эту функцию, однако имеются существенные различия в обработке ими необычных ситуаций и в том, что они определяют как ошибки. Вот пример, показывающий некоторые различия в поведении браузеров, «поддерживающих» const:

 // функция с несколькими объявлениями const для одного и того же имени
 function f() {
    alert('executing within f');
    const x=1;  //объявить постоянную x как 1
    alert(x);
    const x=2;  //переопределить постоянную x как 2
    alert(x);
    x=3;        //попытаться задать 3 для x
    alert(x);
 }
 alert('about to call f'); f();

В зависимости от браузера код может выполнить следующее:

  • сообщить о синтаксической ошибке и не загрузить страницу;
  • выдать исключение при вызове f;
  • Alert: '1', '2', '2'. Это значит, что конфликтующие объявления const разрешены, но назначение проигнорировано;
  • Alert '1', '2', '3'. Это значит, что const обработано точно так же, как var.

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

Фактическая реализация JavaScript не может поддерживать только незначительное количество тривиальных случаев применения. Настоящая реализация должна также что-то делать с нетипичными пограничными случаями и ошибочными сценариями, которые имеются на реальных веб-страницах. Вполне вероятно, стандартизированные спецификации и существуют преимущественно для таких ситуаций. Что делать с простыми распространенными случаями применения, как правило, очевидно. Это для пограничных случаев требуется спецификация стандартов, обеспечивающая существование совместимых реализаций.

TC39 серьезно рассматривал включение const в ES5, оно присутствует в ранних проектах ES5. Однако выявилось много проблем с его определением и взаимодействием с другими объявлениями. В конечном счете, в TC39 было достигнуто соглашение, что стандартизация const подождет «следующую» редакцию спецификации, в которой, возможно, будут решены и некоторые из этих прочих проблем. В сущности, комитет TC39 решил, что стандартизация функции с изъяном нежелательна. Вместо этого он постановил отложить любую стандартизацию const на будущее, когда во все браузеры мог бы быть встроен единый усовершенствованный дизайн.

Так что же IE9 делает с const? До сих пор группа придерживалась решения не поддерживать его. Это пока не консенсусная функция, поскольку никогда не была доступна во всех браузерах. Спецификация стандарта отсутствует, зато имеются существенные семантические различия между всеми существующими реализациями браузеров. В дополнение к этому, известно, что TC39 решил не стандартизировать никакие существующие альтернативы и намеревается заново рассмотреть вопрос в следующих редакциях ECMAScript.

Желание многих разработчиков веб-сайтов иметь возможность использовать такое объявление понятно. Однако не хочется создавать еще одну ситуацию, подобную условному объявлению функции, когда функция должна существовать, но разработчикам веб-сайтов лучше бы не применять ее, если их заботит совместимость браузеров. В конечном счете, представляется, что наилучшее долгосрочное решение для Интернета — исключить его из использования и подождать, пока процесс стандартизации пройдет своим чередом.

Принципиальный подход к принятию решений

Это всего лишь четыре конкретных примера JavaScript, демонстрирующих, решения какого рода приходится принимать в ходе создания IE9. Много схожих проблем возникает в связи с другими стандартами и консенсусными функциями в Интернете. В каждом случае проводится подобный анализ. А что гласит стандарт? Скольким веб-сайтам это действительно требуется? Является ли это консенсусной функцией с общей семантикой? Существует ли несколько несовместимых вариантов функции? Работает ли над этим вопросом комитет по стандартам? Существуют ли какие либо наборы тестов? Будет ли принятие содействовать или препятствовать процессам стандартизации? В конечном счете, это вопрос субъективного суждения, а потому важно приложить все усилия, чтобы обеспечить принципиальный и последовательный подход к принятию решений.

Аллен Уирфс-Брок (Allen Wirfs-Brock),

архитектор языка JavaScript, корпорация Microsoft