Как стать автором
Обновить

Комментарии 43

спасибо — обязательно прочитаю. сейчас просмотрел где-то до середины — интересно :)
Объявить такую функцию можно двумя, по сути, эквивалентными способами.
На самом деле, способов существенно больше. Правда, использовать их я бы не советовал.
+function(){console.log(5)}() // подставьте свой любимый оператор
1,function(){console.log(5)}() // подставьте своё любимое число, строку или ещё какое-нибудь выражение
[function(){console.log(5)}()]


Общее же правило таково: если функция является частью выражения, то она становится FunctionExpression и является выражением, т.е. её можно вызвать. Если же она не является частью выражения, то она является FunctionDeclaration и вызову не поддается. Например:
function fnc(){} // FunctionDeclaration
fnc = function(){} // FunctionExpression
Отсюда же получаем, что следующая запись вполне корректна
fnc = function(){return 5}()

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

По этой теме советую почитать статью на javascript.ru.
Ну и да: тут скорее не объявление, а именно использование.
я может себе неправильно все это представляю но все эти три примера это по сути второй способ. + одновременно изварщение, специально для этогои написал «Если не обращать внимание на всякие извращения» :)

идея была просто в том чтобы показать какраз Function declaration и Function Expression так чтобы на практике было понятно. может не лучшим образом сделал конечно :)
Все эти способы ничем между собой не отличаются, т.к. это всё один и тот же случай с FunctionExpression с различными способами получения выражения.
угу :)
> Правда, использовать их я бы не советовал.
Мне тильда (битовый NOT) нравится в этом плане :)
Ох, а у меня есть еще столько интересных вопросов, но я… боюсь спросить.
спрашивайте :) если знаю и где-нибудь не всплывет какойнибудь косяк, то отвечу :)
«Этого нельзя понять, это можно только запомнить!»
Self defining function — это страшно читаемая вещь, а в обфусцированном коде это вообще как выстрел в голову!
Он на то и обфусцирован, чтобы стрелять в голову.
Выстрел в голову с рикошетом в выстрелившую ногу.
Левую.
А не будет ли правильней в последнем примере с Currying делать
return fn.apply( this, args );

?

Чтобы правильно обрабатывался this и можно было использовать currying и для методов
спасибо огромное. так будет лучше. поправил :)
> Чтобы правильно обрабатывался this
Боюсь, что невероятные похождения this заканчиваются только с кофескриптом
Self defining function — до этой статьи, если бы я увидел подобный код, врядли бы догадался о намерениях писавшего. Так что… применение под вопросом. Но решение да, элегантное.
Не коснулись такой интересной темы как объявление рекурсивных функций. Например, у нас есть тупо факториал, который используется в куче кода:
var factorial = function (n) {
    return n === 1 ? 1 : factorial(n - 1) * n;
};

ну или
function factorial(n) {
    return n === 1 ? 1 : factorial(n - 1) * n;
};

Потом по каким-то причинам нам нужно
var realFactorial = factorial;
factorial = function (n) {
    throw 'please use realFactorial instead';
};

В итоге получается, что и вызовы factorial(5) и realFactorial(5) вываливают ошибку. Это получается, т.к. рекурсивный вызов factorial использует переменную из родительской области видимости, а там она переопределяется. В этом случае надо определять функцию так:
var factorial = function factorial (n) {
    return n === 1 ? 1 : factorial(n - 1) * n;
};

Ну или чтобы не путаться:
var factorial = function f (n) {
    return n === 1 ? 1 : f(n - 1) * n;
};
спасибо огромное — добавил в статью :)
var saySomethingClever;

(function(){
    var appleTest = /Apple/i;
    var googleTest = /Google/i;

    if( appleTest.test(navigator.vendor) ) 
        saySomethingClever = function(){ console.log("I love apples <3"); }
    else if( googleTest.test(navigator.vendor) )
        saySomethingClever = function(){ console.log("android is everything for me <3"); }
    else saySomethingClever = function(){ console.log("i love this unpopular corporation too"); }
})();

saySomethingClever();


Пример тоже так себе. Тогда уж так:

var saySomethingClever = (function(){
    var appleTest = /Apple/i;
    var googleTest = /Google/i;

    if( appleTest.test(navigator.vendor) ) 
        return function(){ console.log("I love apples <3"); }
    else if( googleTest.test(navigator.vendor) )
        return function(){ console.log("android is everything for me <3"); }
    else
        return function(){ console.log("i love this unpopular corporation too"); }
})();

saySomethingClever();


Или, даже так:

var saySomethingClever = (function(){
    var appleTest = /Apple/i,
        googleTest = /Google/i;

    return appleTest.test(navigator.vendor) ? function(){ console.log("I love apples <3")                      } : 
          googleTest.test(navigator.vendor) ? function(){ console.log("android is everything for me <3")       } :
                                              function(){ console.log("i love this unpopular corporation too") } ;
})();

saySomethingClever();


Раз уж рассказываете про такую тему, то сразу можно было привести пример этого-же без copy-paste:

var saySomethingClever = (function(){
    var appleTest = /Apple/i,
        googleTest = /Google/i,
        log = function (message) {
            return function(){ console.log(message) };
        };

    return appleTest.test(navigator.vendor) ? log("I love apples <3"): 
          googleTest.test(navigator.vendor) ? log("android is everything for me <3"):
                                              log("i love this unpopular corporation too");
})();

saySomethingClever();


Как-то так, в общем.
Т.к. третий пример поехал из-за ширины — его можно записать так:

var saySomethingClever = (function(){
    var appleTest = /Apple/i,
        googleTest = /Google/i;

    return appleTest.test(navigator.vendor) ?
               function(){ console.log("I love apples <3")                      } : 
          googleTest.test(navigator.vendor) ?
               function(){ console.log("android is everything for me <3")       } :
               function(){ console.log("i love this unpopular corporation too") } ;
})();

saySomethingClever();
первый пример действительно лучше. на мой взгляд с тренарным оператором код становится в данном случае менее читаемым. а вариант с функцией log хорош в этом случае но в качестве общего примера он неподходит, потому что если в ветках есть какиенибудь отличчия — он летит в трубу.
Мне всегда была непонятна поголовная боязнь тернарного оператора. Он прекрасен для своих целей.
я специально упомянул что в данном случае. возможно это мой пунктик но мне неочень нравится возвращать с его помощью большой кусок кода. тем более что реальных преимуществ при этом никаких
спасибо огромное — заменил пример в саттье на немного исправленный ваш первый.
Спасибо, очень интересно и позновательно. Но использовать их надо аккуратно, а-то потом коллеги по работе будут мучаться, если они не читали этот пост. :)
console.log( "sample b" );
b();

function b() { console.log(1); }

«ну хоть ты тресни», но FF и Chrome показали один и тот же результат
> sample b
> 1
даже если определение функции b() поставить в самом начале
ну да. я вроде бы так и написал, незаню как можно лучше было сформулировать. смысл примера b показать что в случае объявления именованной функции к ней можно обратиться даже до появления непосредственно строки с обьявлением.
Надо так и формулировать, что вызов функции можно осуществлять до её объявления.
Хотя тот же FF сказал «ай-яй-яй»:
ReferenceError: b is not defined
Chrome скушал.

Лично я понял, что вы примером пытались показать, что «1» выведется раньше, чем «sample b»
пс. я с соседом читали статью и поняли одинаково. Может всё-таки дело в формулировке?
Спасибо сейчас уточню формулировку :)
по поводу второго коментария — фаерфокс неможет это нескушать, боюсь потому что это ожидаемое поведение от любого браузера :)
впрочем если вы вбивали это непосредственно через консоль фаербага то да могли быть проблемы.
угу в firebug. Даже если выполняется eval() разве должа быть какая-то разница с обычным кодом, запускаемым из загруженного файла?
сказать по правде я с этим особенно не разбирался. в ближайшем будушем попробую это исправить :) но чисто эмпирически — мне кажется вполне — всетаки поведение именованных функций не совсем обычное. ну и фаербаг не всегда себя идеально ведет так что он может :)
я тут посмотрел на сообщение об ошибке в firebug. скорее всего при прогоне через консоль в фаербаге функция превращается в именованный function expression( это второй вариант в этой статье, только у меня он без имени, думал что так проще будет но вот этот момент упустил из виду конечно. )
ну вот типа этого a = function b() { console.log(2); }. и тогда она ведет себя как положенно для этого случая, но не так как мы ожидаем
Карринг для замыкания строки из двух букв даёт сомнительную экономию текста за счёт производительности.
Вот еслиб там был объект Author и в console.log передавалось author.toString() — жаверы 90ых бы заценили.
А ещё можно соорудить и само-пере-определяемую функцию, если её замкнуть в саму себя…
/** usage: 
   * yield(N) -- (re)start yielding song  
   * yield() -- continue/loop yielding 
   */
var yield = function(more) {
    more = more||Math.ceil(Math.random()*3);
    (function(jack){
        yield = function(nomore) {
            if( nomore || !more--) return (yield = jack)();            
            return "No more!";
            }
    })(yield);
    return "Hit the road, Jack! And don't you come back!";
}
это супер — мне нравится :))))
var f = function() { console.log(1); }
f();

function f(){ console.log(2); }
f();

выдаст 1 и 2, а не две единицы, кстати
а, не, туплю — все верно
тут хук фбага
отлично :) выше просто уже были проблемы с этим — надо бы про фаербаг написать наверно.
вы, кстати, ошибаетесь. при выполнении через консоль может быть такой косяк потому что функция превращается в фанкшен экспрешен. каждый тест перед постом я проверяю как мнимум в двух браузерах.
Очень полезно и познавательно. Спасибо
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.