Переобъявленная глобальная переменная Javascript переопределяет старое значение

На днях я столкнулся с интересной проблемой, и мне было интересно, может ли кто-нибудь пролить свет на то, почему это происходит. Вот что я делаю (для целей этого примера я несколько упростил пример):

  • Я создаю глобальную переменную, используя обозначение квадратных скобок и присваивая ей значение.
  • Позже я объявляю переменную с тем же именем, что и только что созданная выше. Обратите внимание, я не присваиваю значение. Поскольку это повторное объявление той же переменной, старое значение не следует переопределять, как описано здесь: http://www.w3schools.com/js/js_variables.asp

    //create global variable with square bracket notation
    window['y'] = 'old';
    
    //redeclaration of the same variable
    var y;
    
    if (!y) y = 'new';
    
    alert(y); //shows New instead of Old
    
  • Проблема в том, что старое значение действительно переопределяется, и в приведенном выше примере, например. предупреждение показывает «новое» вместо «старое». Почему ?

Я предполагаю, что еще один способ сформулировать мой вопрос заключается в том, чем приведенный выше код отличается с точки зрения семантики от приведенного ниже кода:

//create global variable 
var y = 'old';

//redeclaration of the same variable
var y;

if (!y) y = 'new';

alert(y); //shows Old

Обновление 1. Основываясь на некоторых комментариях и ответах, я перефразирую пример, чтобы он лучше отражал мою первоначальную проблему.

Создайте 2 файла javascript со следующим содержимым: Script1

//create global variable with square bracket notation
window['y'] = 'old';

Скрипт2

//redeclaration of the same variable
var y;

if (!y) y = 'new';

alert(y); //shows New instead of Old in IE

Включите эти 2 файла в свой html файл

<html>
 <head></head>
 <body>

  <script type="text/javascript" src="my.js"></script>
  <script type="text/javascript" src="my2.js"></script>

 </body>
</html>

При открытии этой страницы в Firefox и Chrome отображается предупреждение "старый", что является ожидаемым поведением. Однако в IE 8 страница будет фактически предупреждать «новое»

Вопрос Обновление 2 перемещен сюда: Redeclared глобальная переменная javascript переопределяет старое значение в IE


person Yousuf Haider    schedule 14.04.2010    source источник
comment
Часть, которую вы упростили, должно быть, была там, где весь этот код находится в функции, что дает var y другую область видимости, чем window.y   -  person kennebec    schedule 14.04.2010
comment
Ваше обновление должно быть новым вопросом, особенно потому, что оно зависит от браузера.   -  person Matthew Flaschen    schedule 14.04.2010


Ответы (4)


Когда вы повторно объявили y с помощью var y;, теперь он не определен, поэтому if(!undefined) оценивается как true.

Добавьте еще одно оповещение в свой пример, чтобы увидеть это:

//create global variable with square bracket notation
window['y'] = 'old';

//redeclaration of the same variable
var y;
alert(y); //undefined

if (!y) y = 'new';

alert(y); // new

var не будет инициализировать переменную дважды, но перезапишет переменную, не инициализированную в первый раз (потому что это новая, более локальная переменная), что делает стиль window['y'], добавляя ее к объекту окна. Возьмите это, например:

//create global variable with square bracket notation
window['y'] = 'old';

//redeclaration of the same variable
var y;
alert(y); //undefined

alert(window.y); //old

if (!y) y = 'new';

alert(y); //shows New instead of Old
alert(window.y);​ //still old
person Nick Craver    schedule 14.04.2010
comment
Если это правда, почему второй фрагмент кода не делает то же самое. Второй фрагмент кода предупреждает о «старом» вместо «нового» - person Yousuf Haider; 14.04.2010
comment
Я пытаюсь понять, что вы имеете в виду, говоря, что var не будет инициализировать переменную дважды, но перезапишет ее, не инициализированную в первый раз, что делает стиль окна ['y'], добавляя ее к объекту окна. Означает ли это, что window.y и y - разные объекты. Я думал (возможно, ошибочно), что выполнение window.y или window['y'] означает создание глобальной переменной с именем y. - person Yousuf Haider; 14.04.2010
comment
@Yousuf - В этом разница, var y не то же самое, что window.y, это отдельные переменные, причем var y является более локальной из двух в области видимости. - person Nick Craver; 14.04.2010
comment
@Nick - Итак, вы говорите, что создание свойства объекта Window не эквивалентно объявлению глобальной переменной. Если это так, то можно ли напрямую обращаться к свойствам, объявленным в объекте окна (например, для window.y), или вам нужно использовать window.y для ссылки на них. - person Yousuf Haider; 14.04.2010
comment
@Yousuf - вы можете получить к нему прямой доступ, например: window['y'] = 'new'; alert(y); при условии, что нет другой локальной переменной с таким же именем, вместо этого вы получите эту локальную переменную. ​ - person Nick Craver; 14.04.2010
comment
Ааа! Есть ли где-то, где такие вещи подробно документированы. Спасибо за ответы. - person Yousuf Haider; 14.04.2010
comment
@Yousuf - есть сама спецификация (в зависимости от того, какую версию вы используете, зависит от браузера) ... но я бы предложил это для более практического описания различных случаев, с которыми вы столкнулись здесь: digital-web.com/articles/scope_in_javascript - person Nick Craver; 14.04.2010

Вы не можете «переобъявить» такие переменные в той же области видимости в JS.

var x = "foo"
function a()
{
  alert(x); // undefined
  var x;
}

В функции a переменная x является локальной, поскольку она имеет var x. Не имеет значения, если это происходит до или после использования.

Так же:

function b()
{
  var z = 1;
  if (true)
  {
    var z = 2;
  }
  alert(z); // 2
}

потому что нет такой вещи, как "блочная" область.

person Matthew    schedule 14.04.2010
comment
Последнее утверждение не совсем верно, вы можете использовать let() для получения области видимости блока. - person Nick Craver; 14.04.2010
comment
Вы можете повторно объявить его, если повторное объявление происходит на том же уровне области. var x = "foo"; var x; alert(x); - person Matthew Flaschen; 14.04.2010
comment
@Nick, правда, но я ограничиваюсь подмножеством JavaScript, реализованного во всех основных браузерах. @ Мэтью, что вы ожидаете от этого? - person Matthew; 14.04.2010
comment
@Nick, let — это проприетарное расширение Mozilla, и оно будет работать только на их механизмах JavaScript (TM) 1.7 (SpiderMonkey, Rhino) - person Christian C. Salvadó; 14.04.2010
comment
@CMS - я бы не назвал это проприетарным (поскольку он находится в спецификации 1.7), Mozilla официально управляет javascript ... при условии, что их движки обычно поддерживают новые спецификации раньше других или по той же причине, как здесь. - person Nick Craver; 14.04.2010
comment
@Nick, я называю его проприетарным, потому что он не является частью спецификации ECMAScript, и мы, вероятно, никогда не увидим эти языковые расширения, реализованные в других движках, таких как JavaScriptCore (Safari), V8 (Chrome ), Carakan (Opera), JScript (IE) и т. д. - person Christian C. Salvadó; 14.04.2010
comment
@CMS - я понимаю вашу точку зрения ... хотя простите меня в случае let(), если я надеюсь, что вы ошибаетесь :) Есть много сложных угловых случаев, которые я видел, некоторые из этих дополнений значительно улучшают читабельность / простоту. немного. - person Nick Craver; 14.04.2010

? Я только что проверил ваш код, и он показывает «старый», и я тестировал FF, Chrome, Safari (ПК) и IE8.

Посмотрите здесь: http://jsbin.com/ifare/edit

person RussellUresti    schedule 14.04.2010
comment
Да, если первый фрагмент выполняется на уровне глобальной области видимости, window['y'] эквивалентен var y, поэтому он предупреждает о старом. jsfiddle не запускается в глобальной области видимости, а скорее в функции. Вероятно, исходный код Юсуфа тоже был в функции. См. источник представления: fiddle.jshell.net/yaQYn/show/light - person Matthew Flaschen; 14.04.2010
comment
почему делать window['y']='old' по-разному, когда это делается внутри функции, а не в глобальной области видимости? - person Yousuf Haider; 14.04.2010
comment
Юсуф, window['y'] такой же. Это var y отличается (в функции var y создает новую переменную, которая скрывает глобальную переменную, если вы не используете window) - person Matthew Flaschen; 14.04.2010
comment
Странно, что jsFiddle дает вам новое. Я создал просто простую HTML-страницу локально, чтобы проверить это, и я все еще старею, когда это просто прямо со страницы. - person RussellUresti; 14.04.2010
comment
@Matthew - Верно, но переменная y объявлялась непосредственно внутри файла сценария. Это не было внутри функции. Это то, что я пытаюсь сделать. Поскольку это не было внутри функции, то почему оно переопределяет старое значение (поскольку его следует рассматривать как повторное объявление, если только на основе ответа Ника это две разные переменные) - person Yousuf Haider; 14.04.2010
comment
Юсуф, если вы запустите свой верхний фрагмент (из вопроса) как в файле сценария, он предупредит о старом. - person Matthew Flaschen; 14.04.2010

Оператор var подлежит поднятию, это означает, что когда код входит в контекст выполнения (непосредственно перед фактической средой выполнения), операторы var и function становятся доступными для его охватывающей области.

Ваш код фактически оценивается следующим образом:

Первый пример:

var y;
window['y'] = 'old';

if (!y) y = 'new';

alert(y);

Второй пример:

var y;
y = 'old';

if (!y) y = 'new';

alert(y);

С поднятым оператором var вы видите фактическое поведение кода.

Смотрите также:

person Christian C. Salvadó    schedule 14.04.2010
comment
Я не думал, что подъем происходит между файлами, но я полагаю, что это имеет смысл, поскольку они выполняются в одной и той же области. - person Justin Johnson; 14.04.2010
comment
После некоторых тестов оказалось, что это не так. - person Justin Johnson; 14.04.2010
comment
@Justin, правильно, я ответил до того, как пользователь отредактировал, проблема с несколькими элементами script, похоже, заключается в том, что IE переустановит undefined существующую переменную, когда оператор var используется в отдельном теге script ... - person Christian C. Salvadó; 14.04.2010
comment
А, это имеет больше смысла. Тогда кажется, что это ошибка/новая функция в IE. - person Justin Johnson; 14.04.2010