Почему IE уничтожает переменные window.ABC?

При выполнении следующего блока кода FF и Chrome выводят typeof(hiya) = string, а IE7/8 выводят typeof(hiya) = undefined.

<html>
    <body>
        <script type="text/javascript">
            window.hiya = 'hiya';
        </script>
        <script type="text/javascript">
            if( false ) {
                var hiya = 1;
            }
            document.write( "typeof(hiya) = "+ typeof(hiya) );
        </script>
    </body>
</html>

Каждое из следующих действий устраняет проблему:

  • Объединение всего в один блок <script>.
  • Удаление блока if.
  • Переименование var hiya = 1 в var hiya2 = 1.
  • Переименование var hiya = 1 в window.hiya = 1.
  • Переименование var hiya = 1 в hiya = 1.

Что происходит? Есть ли в IE ошибка области видимости?


person etoleb    schedule 05.01.2011    source источник
comment
Как насчет того, чтобы не писать такие хитрые вещи в первую очередь? ;)   -  person    schedule 05.01.2011
comment
Ха. Проблема в реальном мире, с которой я столкнулся, была намного сложнее, где было условие, которое определяло бы что-то, если бы другая библиотека не была загружена. Это то, что я могу обойти, но мне бы очень хотелось понять, почему это происходит.   -  person etoleb    schedule 05.01.2011
comment
@delnan: Снарк, да? Я думал, что совершенно очевидно, что это не настоящий код, это просто сокращение, которое показывает проблему. Как и все ОП, прежде чем задавать вопрос.   -  person Juan Mendes    schedule 06.01.2011
comment
Я столкнулся с тем же самым с Ext.ns, который создает пространства имен из строк, используя window.‹name›. Где-то еще, в каком-то сгенерированном шаблонном коде из закрытия Google, он также пытался создать в том же пространстве имен (cci.templates) и перезаписывал исходный объект пространства имен cci, потому что он использовал переменную в глобальной области для создания объектов пространства имен. . Сначала я был сбит с толку, так как он будет проверять if (!window.cci) var cci = {}; Мне потребовалось некоторое время, чтобы добраться до сути. Рада, что и вы это теперь понимаете!   -  person Juan Mendes    schedule 06.01.2011


Ответы (3)


IE тупой, он не распознает, что window.varName и var varName в некоторых случаях обращаются к одной и той же переменной.

Когда встречается новый тег сценария, он сначала инициализирует все переменные, объявленные с помощью var. Он не запускает оператор var (часть, которая инициализирует его как «hiya»). Он просто инициализирует его неопределенным. Однако этого не произойдет, если он был ранее объявлен с помощью var.

Если бы ваш код находился в одном теге скрипта, этой ошибки бы не произошло. Кроме того, если бы первое объявление hiya было сделано с помощью var, этой ошибки также не произошло бы.

В частности, во втором теге script IE сначала ищет операторы var, он находит var var hiya = 1; Затем он говорит, что hiya не был инициализирован операторами var ранее (IE глуп, другие браузеры распознают, что window.hiya делает то же самое) и инициализирует hiya, перезаписывая window.hiya перед выполнением любого кода.

Возможные решения:

  • Держите свой код в одном теге скрипта
  • Не инициализируйте переменные с помощью window.hiYa
  • Если вы не контролируете один из сценариев, убедитесь, что сценарий, использующий var, стоит первым.

Последнее замечание, чтобы уточнить, что анализаторы JS делают с вашим кодом. Когда синтаксический анализатор JS видит ваш код, он преобразует его в следующее:

<html>
    <body>
        <script type="text/javascript">
            window.hiya = 'hiya';
        </script>
        <script type="text/javascript">
            // IE is dumb, it doesn't recognize that hiya is already 
            // defined as window.hiya, so it's initialized to undefined here
            var hiya;
            if( false ) {
                hiya = 1;
            }
            document.write( "typeof(hiya) = "+ typeof(hiya) );
        </script>
    </body>
</html>

Итак, если вы поместите все в один тег сценария, код будет таким (после того, как движок JS переместил операторы var наверх), поэтому вы можете видеть, что IE никак не может его испортить, поскольку ваш window.hiya назначение будет после var, который был перемещен вверх.

<html>
    <body>
        <script type="text/javascript">
            var hiya;
            window.hiya = 'hiya';
            if( false ) {
                hiya = 1;
            }
            document.write( "typeof(hiya) = "+ typeof(hiya) );
        </script>
    </body>
</html>
person Juan Mendes    schedule 05.01.2011
comment
Таким образом, выставление переменной через window.foo, а затем объявление var foo в глобальной области видимости в другом контексте выполнения приведет к сбоям в работе IE. JScript — уникальная вещь. Несмотря на то, что это довольно редкий угловой случай, следует помнить об этом. - person Fabrício Matté; 09.10.2012
comment
@FabrícioMatté Это не такой уж угловой случай. Если вы используете несколько фреймворков, и они оба создают пространства имен (одно использует var, а другое использует window[property], вы получите ошибку. Я получил ее, используя Ext-JS и шаблоны закрытия Google вместе. - person Juan Mendes; 30.10.2012
comment
То же самое происходит в IE, если вы вызываете скрипт в одном теге и используете переменную из него в другом теге. Вот так: <script src="myscript.js"></script><script> method.text = "sample"; </script> - person Navigatron; 13.05.2013

С основной проблемой можно ознакомиться здесь http://jsfiddle.net/Raynos/UxrVQ/. еще предстоит выяснить, почему IE перезаписывает window.hiya без проверки.

[Изменить]

Из спецификации. Страница 38:

Для каждого VariableDeclaration или VariableDeclarationNoIn в коде создайте свойство объекта переменной, имя которого является идентификатором в VariableDeclaration или VariableDeclarationNoIn, значение которого не определено, а атрибуты определяются типом кода. Если уже есть свойство объекта переменных с именем объявленной переменной, значение свойства и его атрибутов не изменяются.

Возможным объяснением может быть то, что в глобальной области IE различает объект window и variable object для глобальной области при объявлении переменных. В качестве альтернативы установка свойства для объекта window напрямую может не задавать такое же свойство для объекта variable. Если вы можете найти официальную спецификацию JScript или иметь исходный код IE, мы сможем выяснить, в чем именно заключается причуда.

[/Изменить]

Благодаря @TimDown и @JuanMendes, указывающим, что это проблема с тем, является ли запись свойства в объект окна объявлением переменной.

Проблема:

объявление переменной перемещается в начало блока. Даже если код мертв. В IE по какой-то причине он будет объявлять hiya как локальную переменную, даже если он классифицирует свойство с тем же именем, которое хранится в окне.

Пояснение:

Происходит то, что вы объявляете переменную с именем hiya. Оператор var автоматически удаляется в начало блока. Оператор if не является блоком, а функция. Таким образом, если код никогда не запускается в блоке, переменная все равно объявляется.

В firefox он распознает, что window.hiya является объявлением hiya.

В IE объявление во втором скрипте перезаписывает его

Что он делает на самом деле

В фаерфоксе:

// script block 1
var hiya; // window.hiya counts as a declaration
window.hiya = "hiya"; // set

// script block 2
if (false) hiya = 1;
document.write(...)

In IE:

// script block 1
window.hiya = "hiya";

// script block 2
var hiya; // redeclared here because window.hiya "isn't" a declaration
if (false) hiya = 1; 
document.write(...)

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

person Raynos    schedule 05.01.2011
comment
Да, я объявляю переменную с именем hiya внутри window (что делает ее доступной только как hiya), и когда я добавляю блок if, содержащий локальный оператор var hiya = .., это приводит к тому, что window.hiya не определено (даже если блок if никогда не выполнялся). - person etoleb; 05.01.2011
comment
Строка кода не выполняется. Он по-прежнему анализируется, и объявление переменной обрабатывается во время компиляции, а не во время выполнения. - person Raynos; 05.01.2011
comment
Этот ответ неверен. Если вы запустите свой код в IE, вы увидите, что hiya имеет значение hiya. Ключом к этой проблеме является наличие двух отдельных тегов script. Смотрите мой ответ. - person Juan Mendes; 05.01.2011
comment
@ Raynos Я думал, что сделал. Я запустил ваш код, и он вел себя, как и ожидалось, в IE. оператор var не устанавливает для hiya значение undefined, потому что, как вы сказали, он перемещается в начало сценария. Как я уже сказал, неправильное поведение не возникает, если они не находятся в отдельных сценариях. В вашем примере показан один скрипт. Подробности смотрите в моем ответе. - person Juan Mendes; 05.01.2011
comment
@JuanMendes примеры должны были показать, в каком порядке вы должны об этом думать. Если вы запустите примеры дословно, то в IE он все равно выдаст правильное значение hiya. Примеры были плохим выбором формулировки. - person Raynos; 05.01.2011
comment
Поскольку ваш ответ уже отмечен как правильный, возможно, вы могли бы улучшить свои примеры? Может быть, показать примеры, которые у меня есть в моем ответе? Тогда я мог бы просто удалить свой ответ. Кроме того, я не знаю, что вы имеете в виду, когда говорите о пространстве имен. - person Juan Mendes; 06.01.2011
comment
Этот ответ неверен: каждый элемент <script> рассматривается как отдельная программа во всех браузерах, включая IE. Это тривиально легко доказать: посмотрите на jsfiddle.net/timdown/JdsZX/2. Вопрос более тонкий, хотя я еще не дошел до сути. - person Tim Down; 06.01.2011
comment
@ Тим, я уже упоминал об этом несколькими комментариями выше! Если вы посмотрите на мой ответ (который теперь был отмечен как правильный), вы сможете понять проблему. - person Juan Mendes; 06.01.2011
comment
@Juan: Ваш ответ также не полностью объясняет, что происходит: вокруг решающего бита есть неясность, именно поэтому IE устанавливает hiya как неопределенное во втором сценарии, что противоречит спецификации ECMAScript, если window предполагается глобальный объект. Спецификация говорит, что во время создания экземпляра переменной он должен использовать глобальный объект в качестве объекта переменной, заметить, что объект переменной уже имеет свойство с именем hiya, а затем ничего не делать. Часть назначения не выполняется. Кроме того, мне кажется, что вы не поняли того, о чем я говорил в своих комментариях. - person Tim Down; 06.01.2011
comment
@TimDown, ты прав. Мы можем определить, в чем проблема, но то, что заставляет IE создавать проблему под капотом, с моей стороны является предположением. - person Raynos; 06.01.2011
comment
@TimDown @Raynos: Для меня это явно ошибка в IE, когда он запускает новый тег скрипта. Он должен признать, что window.hiya уже существует, но это не так, я не уверен, какая часть неясна. - person Juan Mendes; 06.01.2011
comment
@Juan: я точно объяснил, что неясно: почему IE (ошибочно, если window является глобальным объектом) устанавливает hiya как неопределенное во втором скрипте? Еще один очевидный вопрос: каковы точные обстоятельства, которые вызывают эту проблему, и где еще может возникнуть эта проблема? Я не знаю ответа на них, и вы, кажется, тоже. - person Tim Down; 06.01.2011
comment
@Tim Ответ таков: это ошибка IE! Точное обстоятельство? Когда вы определяете переменную в первом теге сценария, используя window.varName, а затем у вас есть другой тег сценария, который выполняет var varName в условном выражении, которое никогда не выполняется. У нас определенно проблемы со связью, я не вижу, какая часть моего объяснения неясна. Можете ли вы прокомментировать мой ответ, какая часть все еще не имеет полного смысла? - person Juan Mendes; 06.01.2011

То, с чем вы столкнулись, связано с:

  1. var является утверждением
  2. В JS нет области блока
  3. Операторы выполняются до запуска кода

Итак, что происходит, так это то, что JavaScript выполнит оператор var раньше всего, но он не будет оценивать выражение присваивания, поэтому hiya по умолчанию будет иметь значение undefined.

Как уже заявил Raynos, IE будет выполнять каждый сценарий самостоятельно, поэтому поведение, описанное выше, приведет к тому, что hiya будет неопределенным.

person Ivo Wetzel    schedule 05.01.2011
comment
Ответ Райноса неверен. Все браузеры выполняют каждый элемент <script> как отдельную программу, а не только IE. Проблема более тонкая, чем вы думаете. - person Tim Down; 06.01.2011