Знаете ли вы JavaScript

У Дмитрия Барановского (это автор Raphaël — JavaScript-библиотеки для построения диаграмм) в бложике нашёл чудесный тест на знание JavaScript, привожу его здесь полностью. Попробуйте, я прошёл его довольно посредственно.

Итак, что выведет каждый кусок кода?
if (!("a" in window)) {
    var a = 1;
}
alert(a);
var b = function a(x) {
    x && a(--x);
};
alert(a);
function a(x) {
    return x * 2;
}
var a;
alert(a);
function b(x, y, a) {
    arguments[2] = 10;
    alert(a);
}
b(1, 2, 3);
function a() {
    alert(this);
}
a.call(null);
15 апреля 2009 20:07

Alisey (alisey.myopenid.com)
15 апреля 2009, 21:03

На первый неправильно ответил. Так и не понял как оно работает.

bolk (bolknote.ru)
15 апреля 2009, 21:06, ответ предназначен Alisey (alisey.myopenid.com):

Насколько я это понимаю, интерпретатор смотрит вперёд и создаёт переменные в зоне видимости и только потом выполняет код, но могу и ошибаться.

А как последний работает и какой смысл во втором?

arikon (sergeybelov.ru)
15 апреля 2009, 21:31, ответ предназначен bolk (bolknote.ru):

В первом же всё очевидно? Атрибута «a» в window нет, и ему там неоткуда появится после if, поэтому undefined.

Тайный смысл второй конструкции я совсем не понял.

В третьем для меня было не очевидно, что var не переинициализирует переменную.

В четвёртом логично, что a и arguments[2] — это ссылка на одно и то же значение. Иначе попадаем на память.

В пятом: this в глобальной области указывает на window. Вызов call() у функции с параметром null равнозначно вызову call() без аргументов. А без аргументов вызываемая функция не «биндится» ни к какому объекту (или «биндится» к «объекту по умолчанию» — window).

Я всё так это понимаю.

bolk (bolknote.ru)
15 апреля 2009, 21:38, ответ предназначен arikon (sergeybelov.ru):

Попробуй в первом примере поставить alert внутри if.

arikon (sergeybelov.ru)
15 апреля 2009, 21:45, ответ предназначен bolk (bolknote.ru):

Может я что-то не понимаю, но интерпретатор в if не заходит. И, как бы, не должен.

Вот этот код алертит один раз undefined.
<script type="text/javascript">
if (!("a" in window)) {
    var a = 1;
    alert(a);
}
alert(a);
</script>

bolk (bolknote.ru)
15 апреля 2009, 22:15, ответ предназначен arikon (sergeybelov.ru):

Теперь убери «var» (alert оставь), тебе по прежнему кажется, что ты понимаешь как это работает?

ExH (exh.myopenid.com)
15 апреля 2009, 23:55

Запись var a = 1; в глобальном скопе означает window.a = 1;

Вот ещё забавно:
if (!("a" in window)) {
alert( 'no' );
}
alert(window.a);

Получается что не важно где задаётся var a или window.a, оно инициализируется в любом случае.

Алик Кириллович (www.alik.su)
16 апреля 2009, 01:52

Здесь важно понимать, что конструкция var объявляет переменную в своей лексической области видимости, вне зависимости от того, где конкретно она находится.

Точно также, конструкция function f () {} объявляет функцию во всей лексической области видимости, вне зависимости от того, на какой конкретно строчке находится эта конструкция.

Например, функцию можно объявить уже после ее использования:

alert (f (5))
function f (x) {return (x*x)}

Или даже на той строчке, на которую никогда не ступит нога интерпретатора:

if (false)
  {
  function f (x) {return (x*x)}
  }

alert (f (5))

Главное, чтобы функция или переменная использовалась в той области лексической видимости, где объявлялась.

bolk (bolknote.ru)
16 апреля 2009, 08:56, ответ предназначен Алик Кириллович (www.alik.su):

Я там во втором комменте примерно то же самое и написал :)

bolk (bolknote.ru)
16 апреля 2009, 08:58, ответ предназначен ExH (exh.myopenid.com):

Это одна из первых вещей, которую я узнал о JS — что всё, что в глобальной зоне видимости, создаётся в window.

zeroglif.livejournal.com (zeroglif.livejournal.com)
16 апреля 2009, 09:02

Что выведет второй кусок в IE? Неоднозначный вопрос, в общем.

zeroglif.livejournal.com (zeroglif.livejournal.com)
16 апреля 2009, 09:04, ответ предназначен Алик Кириллович (www.alik.su):

Объявленная функция внутри if(false) по-разному воспринимается браузерами, это своя трактовка стандарта.

astur (astur.net.ru)
16 апреля 2009, 09:16

Второй пример - вуду, однозначно. Хоть кто-нибудь объясните, что там происходит.

shabunc.ya.ru (shabunc.ya.ru)
16 апреля 2009, 09:21, ответ предназначен Алик Кириллович (www.alik.su):

нет, в файрфоксе if'ы обрубят определение функции
здесь это важно понимать )))

Алик Кириллович (www.alik.su)
16 апреля 2009, 09:26, ответ предназначен zeroglif.livejournal.com:

Объявленная функция внутри if(false) по-разному воспринимается браузерами, это своя трактовка стандарта.
Какие браузеры как-то иначе воспринимают эту конструкцию?

bolk (bolknote.ru)
16 апреля 2009, 09:30, ответ предназначен astur (astur.net.ru):

Это вуду не работает в моей «Опере» 10 :)

Алик Кириллович (www.alik.su)
16 апреля 2009, 09:30

Хотя, да.

bolk (bolknote.ru)
16 апреля 2009, 09:31, ответ предназначен zeroglif.livejournal.com:

В IE он выведет тело функции, а вот мой основной браузер (Opera 10) скажет, что там ошибка (нет переменной «а»).

Алик Кириллович (www.alik.su)
16 апреля 2009, 09:52

Да, действительно, в FF функция не будет объявлена, если она определена в любой вложенном блоке: if, while и т.д.

Т.е. такой пример сработает:

alert (f (5))
function f (x) {return (x*x)}

А вот такой уже нет (даже с условием true):

alert (f (5))
if (true)
  {
  function f (x) {return (x*x)};
  }

Однако, если функция находится НЕ во вложенном блоке, то она БУДЕТ объявлена. Даже если до ее строчки интерпретатор заведомо никогда не дойдет:

function main ()
  {
  alert (f (5));
  return;
  function f (x) {return (x*x)};
  }
main ();

Alisey (alisey.myopenid.com)
16 апреля 2009, 11:03

Второй пример - не вуду. Всё зависит от реализации, но в целом так:

b = function a() { ...по имени "a" функцию видно только здесь }

...а здесь undefined (если выше не определили)

bolk (bolknote.ru)
16 апреля 2009, 11:37, ответ предназначен Alisey (alisey.myopenid.com):

Забавно, спасибо. Как я уже говорил, в «Опере» это не так.

astur (astur.net.ru)
16 апреля 2009, 11:52, ответ предназначен Alisey (alisey.myopenid.com):

У меня в FF второй пример не делает вообще ничего. Даже алерт не выбрасывает.

bolk (bolknote.ru)
16 апреля 2009, 12:15, ответ предназначен astur (astur.net.ru):

Возможно, JS-ошибка.

arty (arty.name)
16 апреля 2009, 13:37, ответ предназначен bolk (bolknote.ru):

alisey.myopenid.com написал всё правильно, кроме последней строчки. Да, действительно имя a видно только внутри функции, поэтому снаружи функции такое имя вообще не задано, и опера выдаёт Undefined variable: a. То есть, «такого имени в scope вообще нет», а не «такое имя есть, но значение ему не присвоено»

arty (arty.name)
16 апреля 2009, 13:41, ответ предназначен bolk (bolknote.ru):

а, ну да, показывается это сообщение не в алёрте, а в консоли

имхо какой-то смысл имеют только первые два примера, остальное — знакомство с извращениями

bolk (bolknote.ru)
16 апреля 2009, 14:28, ответ предназначен arty (arty.name):

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

arty (arty.name)
16 апреля 2009, 15:15, ответ предназначен bolk (bolknote.ru):

имхо, примеры, начиная с третьего — ошибки, глумление над программированием, которое нельзя использовать в практике, поэтому всё равно, что как браузеры обрабатывают эти ошибки

1smash1.livejournal.com (1smash1.livejournal.com)
16 апреля 2009, 16:50, ответ предназначен arty (arty.name):

Что бы вы понимали вообще в программировании програм.

Эти примеры нужны для того, чтоб понимать что происходит с вашим кодом и как именно устроен ваш инструмент.

arikon (sergeybelov.ru)
16 апреля 2009, 17:50, ответ предназначен bolk (bolknote.ru):

Да, всё ещё понимаю =)

JS интерпретируется как бы в 2 этапа.
Сначала создаются «глобальные» объекты -- функции, глобальные переменные и т.п. Потом выполняется код. Это и позволяет определять функции в произвольном месте кода, в том числи позже первого использования.

Вижу, об этом выше уже написали.

zeroglif.livejournal.com (zeroglif.livejournal.com)
16 апреля 2009, 18:31, ответ предназначен arikon (sergeybelov.ru):

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

zeroglif.livejournal.com (zeroglif.livejournal.com)
16 апреля 2009, 18:37, ответ предназначен Алик Кириллович (www.alik.su):

Однако, если функция находится НЕ во вложенном блоке, то она БУДЕТ объявлена
Всё так, я просто обратил внимание на то, что и у вас, и у автора теста были приведены неоднозначные (браузерозависимые) примеры. Результат будет зависить от консоли. ;)

bolk (bolknote.ru)
16 апреля 2009, 20:30, ответ предназначен arikon (sergeybelov.ru):

Ну, это мой первый комментарий в этом посте.

arty (arty.name)
16 апреля 2009, 23:34, ответ предназначен 1smash1.livejournal.com:

ой-ой, гуру % )

если вы в своём коде объявляете переменные и функции с одинаковыми названиями, то вы не гуру. То, что браузер не называет это ошибкой, ещё не означает, что так можно делать.

Dmitry Baranovskiy (dmitry.baranovskiy.com)
17 апреля 2009, 04:45, ответ предназначен zeroglif.livejournal.com:

Действительно второй пример в IE отрабатывает неправильно. Можно в начале написать var a = 1; тогда результат будет одинаковый. Там был ещё один пример изначально: alert(1 in [,,2]), но пришлось убрать из-за Firefox. :)
Ещё буквально вчера добавил:
var a = 5;
a.t = 3;
alert(a.t);

sharovatov.livejournal.com (sharovatov.livejournal.com)
17 апреля 2009, 08:33

что интересно, в багзилле наличествует баг про первый кусок кода - https://bugzilla.mozilla.org/show_bug.cgi?id=468096

bolk (bolknote.ru)
17 апреля 2009, 08:57, ответ предназначен Dmitry Baranovskiy (dmitry.baranovskiy.com):

А! Это я знаю. Что в JavaScript два вида типов :)

zeroglif.livejournal.com (zeroglif.livejournal.com)
17 апреля 2009, 09:03, ответ предназначен Dmitry Baranovskiy (dmitry.baranovskiy.com):

буквально вчера добавил
Усилить хочется:
var a = 5;
alert(a.t = 3);
alert(a.t);

Dmitry Baranovskiy (dmitry.baranovskiy.com)
17 апреля 2009, 10:34, ответ предназначен zeroglif.livejournal.com:

Хорошее дополнение :)

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)