Por que posso usar uma função antes de ser definida em JavaScript?

167

Esse código sempre funciona, mesmo em diferentes navegadores:

function fooCheck() {
  alert(internalFoo()); // We are using internalFoo() here...

  return internalFoo(); // And here, even though it has not been defined...

  function internalFoo() { return true; } //...until here!
}

fooCheck();

Não consegui encontrar uma única referência para o porquê de funcionar. Vi isso pela primeira vez na nota de apresentação de John Resig, mas foi mencionada apenas. Não há explicação lá ou em qualquer lugar para esse assunto.

Alguém poderia, por favor, esclarecer-me?

Edu Felipe
fonte
3
Nas versões mais recentes do firefox, isso não funciona se o código estiver em uma tentativa / captura. Veja este violino: jsfiddle.net/qzzc1evt
Joshua Smith

Respostas:

217

A functiondeclaração é mágica e faz com que seu identificador seja vinculado antes que qualquer coisa em seu bloco de código * seja executada.

Isso difere de uma atribuição com uma functionexpressão, que é avaliada na ordem normal de cima para baixo.

Se você alterou o exemplo para dizer:

var internalFoo = function() { return true; };

Pararia de funcionar.

A declaração da função é sintaticamente bastante separada da expressão da função, mesmo que pareça quase idêntica e possa ser ambígua em alguns casos.

Isso está documentado no padrão ECMAScript , seção 10.1.3 . Infelizmente, o ECMA-262 não é um documento muito legível, mesmo para os padrões!

*: a função, bloco, módulo ou script que contém.

bobince
fonte
Eu acho que realmente não é legível. Acabei de ler a seção que você apontou 10.1.3 e não entendi por que as disposições causariam esse comportamento. Obrigado pela informação.
Edu Felipe
2
@bobince Ok, comecei a duvidar de mim mesmo quando não consegui encontrar uma única menção ao termo “içamento” nesta página. Espero que esses comentários tenham o Google Juice ™ suficiente para acertar as coisas :) #
Mathias Bynens
2
Esta é uma combinação popular de perguntas / respostas. Considere atualizar com um link / trecho para a especificação anotada do ES5. (Que é um pouco mais acessível.)
1
Este artigo tem alguns exemplos: JavaScript-Scoping-and-Hoisting
Lombas
Eu descobri que algumas bibliotecas usam a função antes da definição, mesmo alguma linguagem permite oficialmente, ex. Haskell. Para ser honesto, isso pode não ser uma coisa ruim, pois você pode escrever um pouco mais expressivo em alguns casos.
windmaomao
27

É chamado HOISTING - Invocando (chamando) uma função antes de ser definida.

Dois tipos diferentes de funções sobre os quais quero escrever são:

Funções de expressão e funções de declaração

  1. Funções de expressão:

    As expressões de função podem ser armazenadas em uma variável para que eles não precisem de nomes de função. Eles também serão nomeados como uma função anônima (uma função sem nome).

    Para invocar (chamar) essas funções, elas sempre precisam de um nome de variável . Esse tipo de função não funcionará se for chamado antes de ser definido, o que significa que o Hoisting não está acontecendo aqui. Sempre devemos definir a função de expressão primeiro e depois invocá-la.

    let lastName = function (family) {
     console.log("My last name is " + family);
    };
    let x = lastName("Lopez");

    É assim que você pode escrevê-lo no ECMAScript 6:

    lastName = (family) => console.log("My last name is " + family);
    
    x = lastName("Lopez");
  2. Funções da declaração:

    As funções declaradas com a seguinte sintaxe não são executadas imediatamente. Eles são "salvos para uso posterior" e serão executados mais tarde, quando forem chamados (chamados). Este tipo de função funciona se você chamá-lo ANTES ou DEPOIS de onde foi definido. Se você chamar uma função de declaração antes de ser definida, o Hoisting funcionará corretamente.

    function Name(name) {
      console.log("My cat's name is " + name);
    }
    Name("Chloe");

    Exemplo de elevação:

    Name("Chloe");
    function Name(name) {
       console.log("My cat's name is " + name);
    }
Negin
fonte
let fun = theFunction; fun(); function theFunction() {}também funcionará (nó e navegadores) #
fider 15/03/19
14

O navegador lê o seu HTML do começo ao fim e pode executá-lo à medida que é lido e analisado em partes executáveis ​​(declarações de variáveis, definições de funções etc.).

Isso é diferente de outros contextos de programação que processam (compilam) todo o seu código-fonte, talvez o vincule a todas as bibliotecas necessárias para resolver as referências e construa um módulo executável, no qual a execução começa.

Seu código pode se referir a objetos nomeados (variáveis, outras funções etc.) definidos mais adiante, mas você não pode executar o código de referência até que todas as peças estejam disponíveis.

Ao se familiarizar com o JavaScript, você ficará intimamente ciente de sua necessidade de escrever as coisas na sequência correta.

Revisão: Para confirmar a resposta aceita (acima), use o Firebug para avançar pela seção de script de uma página da web. Você o verá pular de uma função para outra, visitando apenas a primeira linha, antes de realmente executar qualquer código.

dkretz
fonte
3

Alguns idiomas exigem que os identificadores sejam definidos antes do uso. Uma razão para isso é que o compilador usa uma única passagem no código-fonte.

Mas se houver vários passes (ou alguns cheques forem adiados), você poderá viver perfeitamente sem esse requisito. Nesse caso, o código provavelmente é primeiro lido (e interpretado) e, em seguida, os links são definidos.

Toon Krijthe
fonte
2

Eu apenas usei JavaScript um pouco. Não tenho certeza se isso ajudará, mas é muito parecido com o que você está falando e pode fornecer algumas dicas:

http://www.dustindiaz.com/javascript-function-declaration-ambiguity/

RailsSon
fonte
O link não está mais morto.
precisa saber é o seguinte
1
O link está morto.
Jerome Indefenzo
Aqui está um instantâneo de archive.org. Parece que o autor derrubou o site inteiro por ter conteúdo desatualizado, não que esta postagem de blog se enquadre nessa categoria.
jkmartindale
1

O corpo da função "internalFoo" precisa ir para algum lugar no momento da análise; portanto, quando o código é lido (também conhecido como análise) pelo interpretador JS, a estrutura de dados da função é criada e o nome é atribuído.

Somente mais tarde, o código é executado, o JavaScript realmente tenta descobrir se "internalFoo" existe e como é e se pode ser chamado etc.

Aaron Digulla
fonte
-4

Pelo mesmo motivo, o seguinte sempre será colocado foono espaço para nome global:

if (test condition) {
    var foo;
}
Andrew Hedges
fonte
8
Na verdade, é por razões muito diferentes. O ifbloco não cria um escopo, enquanto um function()bloco sempre cria um. O verdadeiro motivo foi que a definição de nomes globais de javascript ocorre na fase de compilação, para que, mesmo que o código não seja executado, o nome seja definido. (Desculpe demorou tanto tempo para comentário)
Edu Felipe