felvieira
1/17/2019 - 6:37 PM

Questões JS

Qual a diferença entre as funções var name = function() e function name()?

Vejo em alguns códigos diferentes maneiras de definir uma função, mas desconheço a diferença se é que existe.
Qual a diferença entre estas funções?

Função 1
declaração de função
function teste(valor){
    return (valor > 1) ? true : false;
}
Função 2
expressão de função
var teste = function(valor){
    return (valor > 1) ? true : false;
}

A função 1 é defenida quando o script é analizado(parse time) e a função 2 quando é executado(run-time) 
Exemplo de visibilidade de escopo:
<script>
  funcaoUm(); // Da erro, ainda nao foi definida
  var funcaoUm = function() { }
</script>

<script>
  var funcaoDois = function banana() {
    banana(); // Nao da erro, está no escopo
  }
  banana(); // Da erro, está fora do escopo (exceto no IE)
</script>

<script>
  funcaoTres(); // Nao da erro, é resolvida ao compilar
  function funcaoTres() { }
</script>

______________________________________________________________________________________________


Declaração de Função: function name()
Uma declaração de função define uma variável de função sem a necessidade de atribuição de variável. São constructs independentes, e não podem ser aninhados em blocos que não sejam de função (ver comentário mais abaixo). Seria como um "parente" das declarações de variável. Assim como variáveis precisam ser declaradas com var, funções são declaradas com function.

function bar() {
   return 3;
}

bar() //3
bar  //function

Expressão de Função: var name = function()
Uma expressão de função define uma função como parte de uma expressão sintática maior, geralmente uma atribuição de variável. Funções definidas com expressões de função podem ter nome ou serem anônimas. Não podem iniciar por function, por isso os parênteses são usados na "auto invocação" no exemplo abaixo:

//expressão anônima de função
var a = function() {
   return 3;
}

//expressão nominada de função
var a = function bar() {
   return 3;
}

//expressão "autoinvocada" de função
(function digaOi() {
    alert("Oi!");
})();

Exemplo de visibilidade de escopo:
<script>
  funcaoUm(); // Da erro, ainda nao foi definida
  var funcaoUm = function() { }
</script>

<script>
  var funcaoDois = function banana() {
    banana(); // Nao da erro, está no escopo
  }
  banana(); // Da erro, está fora do escopo (exceto no IE)
</script>

<script>
  funcaoTres(); // Nao da erro, é resolvida ao compilar
  function funcaoTres() { }
</script>

"Funções não podem ser declaradas em blocos que não sejam de código"
Vejamos este código:

if (true) {
  function foo() {
    return 'primeiro';
  }
} else {
  function foo() {
    return 'segundo';
  }
}
foo();
Alguns browsers vão retornar "primeiro", outros "segundo"!

De acordo com a especificação, declarações de função até podem aparecer dentro de blocos de função, mas não dentro de blocos de if, while, for e outros blocos estruturais.

Nestes casos, o que deveria ocorrer é um syntax error, porém praticamente nenhuma das implementações faz isso na prática, e o que é pior, cada uma trata códigos como o do exemplo de uma maneira diferente (com exceção de BESEB e DMDScripot, conforme esta fonte).
A principal diferença é de visibilidade de escopo, que o Bacco demonstrou nos exemplos da resposta dele. Para entender como isso funciona, é preciso entender como funciona o que é chamado de variable hoisting em JavaScript. Se você pensa que variáveis e funções são criadas no ponto do código em que são declaradas, você se engana.

Antes de mais nada, uma breve consideração sobre o funcionamento do escopo em JavaScript: existe o escopo global, e existem os escopos de função. Cada função cria um novo escopo. Se existem funções aninhadas, as mais internas enxergam as variáveis das mais externas, mas não o contrário. A resposta do mgibsonbr sobre closures ilustra bem isso:

function x(a1) {          // "x" tem acesso a "a"
    var a2;
    function y(b1) {      // "y" tem acesso a "a" e "b"
        var b2;
        function z(c1) {  // "z" tem acesso a "a", "b", e "c"
            var c2;
Cada vez que uma função é invocada, é criado um contexto de execução para ela, onde ficam guardadas as variáveis que ela define. No caso da função x acima, o contexto de execução contém referências para a1, a2 e y (respectivamente, um parâmetro, uma variável, e uma função). Agora, considere o seguinte exemplo:

function x(a) {
    var a = "foo";
    function a() {
        // nada
    }
    alert(typeof a);
}
x(10);
O que vai sair no alert? O tipo "number" do argumento passado, o tipo "string" da variável, ou o tipo "function" da função? Resposta: "string" (teste você mesmo). Nessa hora, você coça a cabeça e diz:

Mas como assim?! A função foi declarada depois da variável! Devia dar "function"!

É, faz sentido o que você pensou. Acontece que o algoritmo responsável pelo binding dos argumentos, variáveis e funções ao contexto de execução faz isso numa ordem bem específica, que é a seguinte:

Argumentos
Declarações de funções
Variáveis
Se existirem nomes iguais entre argumentos, declarações de funções e variáveis, eles serão sobrescritos obedecendo a essa ordem. No nosso exemplo, a começa como o número 10, depois é sobrescrito pela função, e por fim é sobrescrito pela variável (antes da atribuição). Isso tudo acontece antes de qualquer código ser executado na função, incluindo a atribuição do valor "foo" a a. Em código, o que ocorre é algo assim:

function x(a) {
    function a() {
        // nada
    }
    var a; // undefined, por enquanto
    a = "foo";
    alert(typeof a); // "string"
}
x(10);
Repare que a declaração da variável é separada da atribuição. Diz-se que a declaração é "erguida" ou "içada" (hoisted) para o topo do escopo. Veja mais este exemplo:

function f() {
    var a = 10;
    var b = 10;
    function c() {}
    var d = function() {};
}
Isso na verdade é interpretado assim:

function f() {
    function c() {}
    var a = undefined, b = undefined, d = undefined;
    a = 10;
    b = 10;
    d = function() {};
}
Olha que coisa! A função c está disponível desde o início, mas a função d não está! Isso porque c foi criada via declaração de função, enquanto a função em d foi criada via expressão de função (declarações sempre têm nome, e sempre começam com function como a primeira coisa na linha, desconsiderando os espaços). Se quisermos invocar c antes da linha onde ela é declarada, não há problema, já que o hoisting faz com que ela já esteja disponível:

function f() {
    var a = 10;
    var b = 10;
    c(); // SEM PROBLEMAS!
    function c() {}
    var d = function() {};
}
O mesmo não é válido para d: só vai haver uma função em d após a linha onde é feita a atribuição. Veja:

function f() {
    var a = 10;
    var b = 10;
    d(); // TypeError! Estamos tentando chamar undefined como função
    function c() {}
    var d = function() {};
}
Portanto, respondendo à pergunta: var name = function() e function name() são diferentes porque a primeira é uma declaração de variável, à qual é atribuída uma função, enquanto a segunda é uma declaração de função. Isso tem consequências em relação ao momento que a função estará disponível para ser invocada.
Closure ("clausura" em português, mas esse termo é raramente utilizado), se refere à forma como funções definidas dentro de um "contexto léxico" (i.e. o corpo de uma função, um bloco, um arquivo fonte) acessam variáveis definidas nesse contexto.

Em JavaScript, apenas funções definem um novo contexto léxico (outras linguagens têm regras diferentes - algumas sequer suportam o conceito de closure):

var a = 10; // Mesmo "a" para script1.js, script2.js, etc (efetivamente, uma global)
function f() {
    var b = 20; // Um "b" diferente para cada invocação de f
    if ( x ) {
        var c = 30; // Mesmo "c" dentro e fora do if (i.e. o contexto é "f", não o bloco if)
E cada novo contexto criado dentro (inner) de um contexto já existente tem acesso a todas as variáveis definidas no "de fora" (outer):

function x(a1) {          // "x" tem acesso a "a"
    var a2;
    function y(b1) {      // "y" tem acesso a "a" e "b"
        var b2;
        function z(c1) {  // "z" tem acesso a "a", "b", e "c"
            var c2;
É importante observar que não importa quando a função interna irá executar, nem qual o valor as variáveis externas tinham no momento em que o objeto função foi criado (em contraste com a definição da função, que é em tempo de compilação/interpretação). O que importa é que ambas compartilham a mesma variável, e escritas de um lado refletirão nas leituras do outro e vice-versa.

Pitfalls
Um exemplo de erro comum envolvendo closures é a criação de uma função dentro de um bloco for:

for ( var i = 0 ; i < elementos.length ; i++ ) {
    elementos[i].onclick = function() {
        alert("Esse é o elemento " + i);
    }
}
Esse código não funciona como esperado, uma vez que a variável i utilizada pela função anônima é o mesmo i do contexto externo - o que significa que quando o i externo muda, o valor que a função interna vai acessar é diferente. No final, i será igual a elementos.length (ex.: 10), de modo que clicar em qualquer elemento sempre imprimirá "Esse é o elemento 10".

Uma possível solução para esse problema é a criação de um novo contexto léxico que "capture" o valor daquela variável no momento desejado:

for ( var i = 0 ; i < elementos.length ; i++ )
    (function(i) {
        elementos[i].onclick = function() {
            alert("Esse é o elemento " + i);
        }
    })(i);
Dessa forma, o i parâmetro da função não é o mesmo i usado pelo laço for - e ele possui o valor que a variável tinha no momento da execução.

Utilidade
Existem muitas vantagens em se usar closures, como exemplificado na resposta do @Jordão (que demonstra um meio de se implementar currying). Um outro exemplo seria simular variáveis privadas - algo que não é normalmente suportado pela linguagem JavaScript:

function MeuObjeto() {
    this.publico = { ... }
    var privado = { ... }
    this.foo = function() {
        return privado.a;
    }
    this.bar = function(x) {
        privado.a = x;
    }
    ...
}
var obj = new MeuObjeto();
obj.publico; // Acessível
obj.privado; // undefined
Note que, como não existe nenhuma referência direta para privado, esse objeto não pode ser manipulado diretamente (apenas indiretamente por meio de foo e bar). Mas como foo e bar foram criados dentro do contexto léxico do construtor, elas têm acesso às variáveis locals do mesmo, podendo acessá-las normalmente.

Um outro exemplo "clássico" é o Accumulator Generator, citado num artigo do Paul Graham (em inglês) onde se discute o poder de expressividade relativa das diversas linguagens de programação. O requisito é simples:

Escreva uma função foo que recebe um número n e retorna uma função que recebe um número i, e retorna n incrementado de i.

Nota: (a) [o argumento] é um número, não um inteiro. (b) é incrementado de, não mais.

A solução proposta, com exemplos de uso:

function foo (n) { 
    return function (i) { 
        return n += i;
    } 
}

var x = foo(10);
x(2); // 12
x(3); // 15

var y = foo(20);
y(5); // 25
y(2); // 27
Como os exemplos no final do artigo mostram, linguagens que não suportam closures acabam sendo muito mais varbosas (exigindo muito código para se fazer pouco), de modo que demoram mais para serem escritas, lidas, podem conter mais bugs (já que a probabilidade de bugs aumenta com a quantidade de linha de código), etc.