В JavaScript, как известно, есть два способа объявить функцию.
/* Function Declaration. Функция сразу определяется в локальном контексте под именем func */ function func() { } |
и
/* Function Expression. */ var func; // func просто переменная. Изначально в ней, как обычно, undefined. func = (function () {}); // В определённый момент в качестве значения ей присваивается объект функции. |
FE ещё называют анонимной функцией.
И есть ещё один подвид Function Expression. Именованное (Named) Function Expression:
var func; func = (function fufunc() { /* ... */ }); |
Анонимная неанонимная функция :).
В нашем локальном контексте по прежнему есть переменная func
.
Но в контексте вызываемой функции также появляется её «имя» fufunc
.
var func; func = (function fufunc() { return func === fufunc; // func и fufunc ссылаются на один и тот же объект функции })(); func === fufunc; // ошибка, в этом контексте нет fufunc |
Нужно это, в первую очередь для того, чтобы анонимная функция могла обращаться к самой себе (наш любимый arguments.callee
в последнем стандарте упразднили):
(function iam(x) { if (x > 0) { return iam(x - 1); } else { return 1; } })(5); |
Ну и ещё, это полезно для отладки, чтобы в консоли и профайлере отображалась не просто стена из «(anonym function)», а что-то осмысленное.
И, как всегда, нашу идилию разрушает ёбаный осёл!
IE 9 уже работает с этим по человечески, но более старые, как всегда жгут.
var func; func = (function fufunc() { return func === fufunc; // false })(); func === fufunc; // нет ошибки, но false |
Он создаёт в нашем контексте и func
и fufunc
.
Так не просто создаёт две ссылки на один объект функции. Это можно ещё понять и простить. Нет, это две совершенно различные функции. Да ещё и с различными прототипами.
Например, создаём функцию-конструктор и проверяем в ней, как её вызвали, с new
или без:
function getConstruct(x) { return function C() { if (!(this instanceof C)) { // вызвали без new throw new Error("Example: instance = new C()"); } this.x = x; } } var Construct = getConstruct(); var x = new Construct(); |
Хуя с два. В IE8 this
будет не instanceof C
. Так как C и то, что вернула getConstruct(), это две совершенно разные функции.
Или вот:
var Func = function iam() { iam.counter -= 1; if (iam.counter === 0) { alert("BANG"); } }; Func.counter = 5; |
Хрена. Func и iam два разных объекта и у них два разных свойства counter
.
Или собираем функции в namespace, чтобы не загадить глобальный контекст:
var MyNS = { 'my_super_func': function my_super_func() { /* ... */ }, 'my_puper_func': function my_puper_func() { /* ... */ } }; |
IE8 всё равно загадит глобальный контекст копиями всех этих функций.
Так и живём.
Прикольно, спасибо за статью, пригодится!
adw0rd, 13.03.2013, 8:24
Я знаю для решения этой проблемы отличный хак.
Не поддерживать IE < 9.
Алексей, 13.03.2013, 16:12