Знаете ли вы 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);
На первый неправильно ответил. Так и не понял как оно работает.
Комментарий для alisey.myopenid.com:
Насколько я это понимаю, интерпретатор смотрит вперёд и создаёт переменные в зоне видимости и только потом выполняет код, но могу и ошибаться.
А как последний работает и какой смысл во втором?
Комментарий для Евгения Степанищева:
В первом же всё очевидно? Атрибута «a» в window нет, и ему там неоткуда появится после if, поэтому undefined.
Тайный смысл второй конструкции я совсем не понял.
В третьем для меня было не очевидно, что var не переинициализирует переменную.
В четвёртом логично, что a и arguments[2] — это ссылка на одно и то же значение. Иначе попадаем на память.
В пятом: this в глобальной области указывает на window. Вызов call() у функции с параметром null равнозначно вызову call() без аргументов. А без аргументов вызываемая функция не «биндится» ни к какому объекту (или «биндится» к «объекту по умолчанию» — window).
Я всё так это понимаю.
Комментарий для sergeybelov.ru:
Попробуй в первом примере поставить alert внутри if.
Комментарий для Евгения Степанищева:
Может я что-то не понимаю, но интерпретатор в if не заходит. И, как бы, не должен.
Вот этот код алертит один раз undefined.
<script type=«text/javascript»>
if (!(«a» in window)) {
var a = 1;
alert(a);
}
alert(a);
</script>
Комментарий для sergeybelov.ru:
Теперь убери «var» (alert оставь), тебе по прежнему кажется, что ты понимаешь как это работает?
Запись var a = 1; в глобальном скопе означает window.a = 1;
Вот ещё забавно:
if (!(«a» in window)) {
alert( ’no’ );
}
alert(window.a);
Получается что не важно где задаётся var a или window.a, оно инициализируется в любом случае.
Здесь важно понимать, что конструкция var объявляет переменную в своей лексической области видимости, вне зависимости от того, где конкретно она находится.
Точно также, конструкция function f () {} объявляет функцию во всей лексической области видимости, вне зависимости от того, на какой конкретно строчке находится эта конструкция.
Например, функцию можно объявить уже после ее использования:
alert (f (5))
function f (x) {return (x*x)}
Или даже на той строчке, на которую никогда не ступит нога интерпретатора:
if (false)
{
function f (x) {return (x*x)}
}
alert (f (5))
Главное, чтобы функция или переменная использовалась в той области лексической видимости, где объявлялась.
Комментарий для www.alik.su:
Я там во втором комменте примерно то же самое и написал :)
Комментарий для exh.myopenid.com:
Это одна из первых вещей, которую я узнал о JS — что всё, что в глобальной зоне видимости, создаётся в window.
Что выведет второй кусок в IE? Неоднозначный вопрос, в общем.
Комментарий для www.alik.su:
Объявленная функция внутри if(false) по-разному воспринимается браузерами, это своя трактовка стандарта.
Второй пример — вуду, однозначно. Хоть кто-нибудь объясните, что там происходит.
Комментарий для www.alik.su:
нет, в файрфоксе if’ы обрубят определение функции
здесь это важно понимать )))
Комментарий для zeroglif.livejournal.com:
Какие браузеры как-то иначе воспринимают эту конструкцию?
Комментарий для astur.net.ru:
Это вуду не работает в моей «Опере» 10 :)
Хотя, да.
Комментарий для zeroglif.livejournal.com:
В IE он выведет тело функции, а вот мой основной браузер (Opera 10) скажет, что там ошибка (нет переменной «а»).
Да, действительно, в 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 ();
Второй пример — не вуду. Всё зависит от реализации, но в целом так:
b = function a() { ...по имени «a» функцию видно только здесь }
...а здесь undefined (если выше не определили)
Комментарий для alisey.myopenid.com:
Забавно, спасибо. Как я уже говорил, в «Опере» это не так.
Комментарий для alisey.myopenid.com:
У меня в FF второй пример не делает вообще ничего. Даже алерт не выбрасывает.
Комментарий для astur.net.ru:
Возможно, JS-ошибка.
Комментарий для Евгения Степанищева:
alisey.myopenid.com написал всё правильно, кроме последней строчки. Да, действительно имя a видно только внутри функции, поэтому снаружи функции такое имя вообще не задано, и опера выдаёт Undefined variable: a. То есть, «такого имени в scope вообще нет», а не «такое имя есть, но значение ему не присвоено»
Комментарий для Евгения Степанищева:
а, ну да, показывается это сообщение не в алёрте, а в консоли
имхо какой-то смысл имеют только первые два примера, остальное — знакомство с извращениями
Комментарий для arty.name:
Возможно. Очень обидно, что в таких вещах браузеры ведут себя по-разному. Можно наступить на большое количество граблей.
Комментарий для Евгения Степанищева:
имхо, примеры, начиная с третьего — ошибки, глумление над программированием, которое нельзя использовать в практике, поэтому всё равно, что как браузеры обрабатывают эти ошибки
Комментарий для arty.name:
Что бы вы понимали вообще в программировании програм.
Эти примеры нужны для того, чтоб понимать что происходит с вашим кодом и как именно устроен ваш инструмент.
Комментарий для Евгения Степанищева:
Да, всё ещё понимаю =)
JS интерпретируется как бы в 2 этапа.
Сначала создаются «глобальные» объекты -- функции, глобальные переменные и т. п. Потом выполняется код. Это и позволяет определять функции в произвольном месте кода, в том числи позже первого использования.
Вижу, об этом выше уже написали.
Комментарий для sergeybelov.ru:
Это не только глобального контекста касается, внутри функций всё происходит аналогичным образом.
Комментарий для www.alik.su:
Всё так, я просто обратил внимание на то, что и у вас, и у автора теста были приведены неоднозначные (браузерозависимые) примеры. Результат будет зависить от консоли. ;)
Комментарий для sergeybelov.ru:
Ну, это мой первый комментарий в этом посте.
Комментарий для 1smash1.livejournal.com:
ой-ой, гуру % )
если вы в своём коде объявляете переменные и функции с одинаковыми названиями, то вы не гуру. То, что браузер не называет это ошибкой, ещё не означает, что так можно делать.
Комментарий для zeroglif.livejournal.com:
Действительно второй пример в IE отрабатывает неправильно. Можно в начале написать var a = 1; тогда результат будет одинаковый. Там был ещё один пример изначально: alert(1 in [,,2]), но пришлось убрать из-за Firefox. :)
Ещё буквально вчера добавил:
var a = 5;
a.t = 3;
alert(a.t);
что интересно, в багзилле наличествует баг про первый кусок кода — https://bugzilla.mozilla.org/show_bug.cgi?id=468096
Комментарий для dmitry.baranovskiy.com:
А! Это я знаю. Что в JavaScript два вида типов :)
Комментарий для dmitry.baranovskiy.com:
Усилить хочется:
var a = 5;
alert(a.t = 3);
alert(a.t);
Комментарий для zeroglif.livejournal.com:
Хорошее дополнение :)