JavaScript: баг с замыканием в FireFox
Какой alert должен показать следующий JavaScript в браузере?
<html>
<head>
<script>
/* When are functions defined? */
function really() { alert("Original"); }
if (0) {
alert("No");
function really() { alert("Yes, really"); }
}
really();
</script>
</head>
<body>Really</body>
</html>Поведение тут зависит от браузера, FF выведет «Original», а остальные браузеры — «Yes, really».
Комментарии 29
http://dmitry.baranovskiy.com/post/36156571
really();
function really() { alert(1) }
if (1) function really() { alert(2) }
If не создаёт новой области видимости, значит второе определение должно перетирать первое. И Firefox ведёт себя неправильно.
Привязка переменных должна делаться только в момент входа в контекст выполнения:
lol = 1; (function () { alert(lol); var lol; })();
В PHP, конечно, царит шизофрения. Например, функции внутри if создаются по мере выполнения кода, а функции после безусловного exit — в момент инициализации.
OMG! Я всегда считал, что function really() {} полный эквивалент var really = function () {}
Замыкание — это функция внутри функции со своим окружением. То, что здесь окружение внутри функции не используется, мне кажется частным случаем замыкания.
Кстати, ты не заглядывал в стандарт? FF точно себя неправильно ведёт?
Комментарий для Евгения Степанищева:
Причём здесь лямбды? Речь о порядке инициализации и области видимости. И замыкание — это не «функция внутри функции». Замыкание — это функция, с привязкой переменных на основе лексического контекста. Она может быть совсем даже снаружи, а не внутри.
Некоторые вообще предпочитают называть замыканием не саму функцию, а только эту привязку.
i = 1;
function really() { alert(i) };
function lol(){ var i = 2; really();} // единица, замыкание есть, «функций внутри функции» нет
lol();
Комментарий для alisey.myopenid.com:
Я упрощённо говорю, ага.
Я подумал, что речь идёт о create_function, только сейчас догадался, что речь идти может и о function вложенном в function.
Меня волнует моё невольно заблуждение. Я всегда считал, что два упоминаемых описания эквиваленты в языке. По стандарту это так или нет?
По стандарту это не так, что является хорошим дизайнерским решением.
Как это работает: попадаем в новую область видимости, в этом конкретном случае Global. […много предварительных действий…], ищем декларации функций. function a()… — это декларация. Запоминаем привязку имя -> тело функции. Больше объявлений функций нет, теперь ищем переменные. Есть объявление переменной a. Если бы это имя уже не ссылалось на функцию, мы бы инициализировали его undefined, а так — пропускаем.
Теперь, когда все имена привязаны, начинаем выполнять код. a() выдаёт единицу, что подтверждает идею о том, что декларации функций обрабатываются до начала выполнения кода. Доходим до строки с присвоением: a = …, в результате присвоения «a» начинает ссылаться на анонимную функцию, и следующая строка выдаёт 2.
Это вроде бы очевидно — присвоение происходит в момент выполнения кода.
Но может быть неочевидно, что вот это тоже присвоение: var a = function(){ }
И это: a = true ? alert : 9; a(5);
Функции — такие же значения как числа и строки.
Можно в массив запихнуть: [function() { alert(1) }, function() { alert(2) }][1]();
А вот у деклараций (и переменных, и функций) есть та особенность, что они инициализируются в момент вхождения в новую область видимости. В вашем первом примере if не создаёт новой области видимости, это можно проверить задав в нём
Я
P.S. заметку поправил.
Рассказывать что есть такой тип «функция» не нужно. Из традиционного, я давно пишу на JS, писал на Pascal, Perl, где есть такой тип и недавно — на Python.
В Пайтоне и некоторых версиях Javascript ещё есть yield, вроде базовые вещи, не понимаю как большинство «прекрасно обходится и без них».
В Python/JS 2.0 есть yield, да. Это удобно.
Ну вот, всё уже объяснили ) Положу свою копейку:
В JavaScript,
Function statement хорош тем, что может быть использован до появления — почему уже описали. Т.е. даже function a() { return b(); function b() { return 1; }; }; отработает и вернёт 1.
Function expression создаёт анонимную функцию, но наша анонимная функция может иметь имя: var x = function y() { alert(x); alert(y); };, притом о настоящем имени функции никто, кроме неё не знает.
Firefox вообще говоря, прав, исходя из написанного на mdc определения function expression:
A function declaration is very easily (and often unintentionally) turned into a function expression. A function declaration ceases to be one when it either:
* becomes part of an expression
* is no longer a «source element» of a function or the script itself. A «source element» is a
var x = 0; // source element
if (x == 0) { // source element
x = 10; // not a source element
function boo() {} // not a source element
}
function foo() { // source element
var y = 20; // source element
function bar() {} // source element
while (y == 10) { // source element
function blah() {} // not a source element
y++; // not a source element
}
}
https://developer.mozilla.org/en/Core_Java … /Functions
http://mxr.mozilla.org/mozilla/source/js/narcissus/
console.log(evaluate('function a() { return 1 }; if (0) function a() { return 2 }; a();'));
Действительно возвращает единицу. Не могу найти в коде, как это происходит.
Даже в JS 1.7, их можно вывернуть наизнанку и сделать
var event = yield waitForMouseClick();
alert(event.x);
Впору переименовывать заметку :)
Мне вот больше интересно, как это так:
function a() { return 1 }; if (1) { function a() { return 2 }; } a(); => 2
Это в глобальной области видимости.
А теперь
(function() {
function a() { return 1 };
if (1) function a() { return 2 };
return a();
})(); // 1
Так какой же код официально «дурацкий»?IE-style, Mozilla-style?
(function() {
function a() { return 1 };
if (1) function a() { return 2 };
return a();
})(); // 1 в FF3
Если считать, что вторая функция a() — это Declaration, то она переопределяет первую на этапе инициализации, результат 2. Если считать её Expression — она переопределяет первую на этапе выполнения. (Обратите внимание, условие выполняется). Опять результат 2.
Движок Брендана Айка выдаёт 2, зато SpiderMonkey — единицу. Непонятно что это за «экстеншен» такой.
Кстати :) По вашим комментариям на dklab понял как работают прототипы и конструкторы. Спасибо.
Так понятней выйдет:
(function() {
if (1) function a() { return 2 };
return a();
function a() { return 1 };
})();
Почему так выходит — скорее всего
// function statement
function a() {
// function statement
function b() {}
if (0) {
// function expression
function c() {}
}
}
Синтаксис JS я знаю. Я уже объяснил что для меня было непонятным.
Именно.
А если считать, что первая — declaration, а вторая — expression? Я там выше привёл пример.
Да, рассмотрим её как Exression, то есть
if (1) var a = function () { return 2 };
Окажется, что если перевести в такую форму, то результат станет 2, вполне ожидаемый результат, выражение обработалось, значение присвоилось. Почему тогда в первом случае не так?
Вы можете сказать, что это не совсем правильная аналогия, а правильно будет
if (1) function /a/ () { return 2 };
Вроде бы тогда всё логично, функция есть, но она не используется. Результат 1.
Но есть одна вещь, которая ломает эту теорию:
(function() {
//function a() { return 1 };
if (1) function a() { return 2 };
return a();
})(); // 2!!!
Ещё один эксперимент, убираем обёрточую функцию:
function a() { return 1 };
if (1) function a() { return 2 };
a(); // 2
Результат снова два. Я не вижу логики.