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

Код:
//создать именованную функцию
function a() { console.log(1); }

//создать анонимную функцию, а затем присвоить ее переменной
b = function() { console.log(2); }

Разница между ними кажется небольшой, но это только кажется.
Что будет если выполнить этот код?

Код:
console.log( "sample b" );
b();

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

правильно — в консоли появится сначала текст с названием примера, а потом единичка, потому что все именованные функции переносятся в начало области видимости, и вызывать их можно когда угодно.
Усложняем задачу

Код:
console.log( "sample c" );

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

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

Но нас ведь так просто не запутать. Функции переносятся в начало ровно в том порядке, в котором они были созданы, то есть в консоли будет 2.
Ладно, а так?

Код:
console.log( "sample d" );
d();
var d = function() { console.log(2); }

Ответ: а тут правила другие — это вообще не заработает, потому что значение переменной на момент вызова — undefined.

А если так?

Код:
console.log( "sample e" );
var e = function(){ console.log(1); } 
    
e();

e = function() { console.log(2); }

Ну тут уже все ясно, да — на момент вызова е содержит в себе первую функцию, так что в консоли будет единичка.
А теперь настало время для самого сложного вопроса *барабанная дробь*

А что будет тут ?

Код:
console.log( "sample f" );
var f = function() { console.log(1); }
f();

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

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

Immediate functions

За этим названием скрываются «одноразовые» функции — они не имеют имени, и выполняются сразу после своего объявления.

Зачем это нужно? По большей части за тем, чтобы не засорять глобальное пространство имен. Например, если надо проинициализировать что-нибудь, и при инициализации нам потребуется парочка переменных которые потом вообще совсем не нужны.

Объявить такую функцию можно двумя, по сути, эквивалентными способами.

Код:
console.log("//immidiate function");

//sample a
(function(){
    console.log( "a" );
})();

//sample b
(function(){
    console.log( "b" );
}());

Отличий между ними нет никаких.

Init time branching

Иногда случается, что значение функции зависит от какого-нибудь значения, которое после инциализации не меняется. ну что-нибудь вроде этого:

Код:
// ПЛОХОЙ пример
function saySomethingClever(){
    var appleTest = /Apple/i;
    var googleTest = /Google/i;

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

saySomethingClever();

Как мог заметить внимательный читатель, здесь используется ещё и immidiate function. Теперь проверка выполняется ровно один раз.

Self defining function

Иногда бывает так, что при первом вызове функции нужно выполнить какие-нибудь дополнительные действия. Реализовать это можно следующим образом:

Код:
var selfDefining = function()
{
    console.log("some really heavy initialization occured");
    console.log("f*ck yeah!");
    selfDefining = function(){
        console.log("job done!");
    }
}
selfDefining();
selfDefining();

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

Currying

С помощью этого приема можно создать частный вариант какой-нибудь довольно общей функции. Реализация выглядит так:

Код:
function curry( fn ){
    var slice = Array.prototype.slice,
        storedArgs = slice.call( arguments, 1 );

    return function() {
        var args = storedArgs.concat( slice.call( arguments ) );
        return fn.apply( this, args );
    }
}

Пока конечно нифига непонятно, но это нормально. Сейчас все объясню.
Допустим у нас есть функция которая печатает сообщение.

Код:
function printMessage( author, message ){
    console.log( author + " say: " + message )
}

Но в данном модуле проекта в качестве значения author всегда используют me, так что нам очень желательна более частная её версия принимающая только строчку с сообщением, а автора вписывающая сама.

Код:
var printMyMessage = curry( printMessage, "me" );
printMyMessage( "I would like to tell you about birds and bees in js world" );

И теперь она у нас есть.