Vale lembrar que tem algumas diferenças entre eles.

Por exemplo, uma function declaration pode aparecer depois que esta é chamada, e funciona normalmente devido a um processo chamado hoisting (basicamente, é como se o interpretador "movesse" algumas declarações para o topo). Exemplo:

f(); // olá

function f() {
    console.log('olá');
}

Apesar de estar chamando a função antes da sua declaração, o código acima funciona normalmente (devido ao hoisting, a declaração é avaliada antes).

Mas function expressions não sofrem os efeitos do hoisting, então assim já não funciona:

f(); // TypeError: f is not a function

var f = function () {
    console.log('olá');
}

Mesmo se eu usasse uma named function expression (var f = function f() { etc), também daria o mesmo erro. E com arrow function, também não funcionaria, pois para estas também não é feito o hoisting.


E tem ainda um outro tipo (se bem que podemos considerar um "sub-tipo"), que é o IIFE (Immediately Invoked Function Expression), quando você declara a função e já a executa:

(function (texto) {
    console.log('olá', texto);
})('mundo'); // olá mundo

Por fim, sobre as diferenças entre function e arrow functions, não deixe de ler a documentação.

Faltou a forma IIFE, obrigado por ressaltar isso e em relação ao hosting também, parabéns pelo comentário 👾