Pular para o conteúdo principal

[Medium] 📄 Hoisting

1. What's Hoisting ?

A execução do JS pode ser dividida em duas fases: a fase de criação é a fase de execução:

var name = 'Pitt';
console.log(name); // print Pitt

Devido à característica do Hoisting, o código acima deve ser entendido como: primeiro a variável é declarada e depois o valor é atribuído.

// create
var name;

// execute
name = 'Pitt';
console.log(name);

As funções são diferentes das variáveis — elas são alocadas na memória durante a fase de criação. A declaração de função é a seguinte:

getName();

function getName() {
console.log('string'); // print string
}

O código acima consegue executar normalmente e imprimir o console.log sem gerar erro por causa da seguinte lógica: a function é primeiro elevada ao topo, e só depois a chamada da function é executada.

// create
function getName() {
console.log('string');
}

// execute
getName();

No entanto, é preciso notar que essa característica de Hoisting exige atenção à ordem de escrita ao usar expressões.

Na fase de criação, a function tem a maior prioridade, seguida pelas variáveis.

Correct

name = 'Yumy';
console.log(name); // print Yumy
var name;

// --- Equal to ---

// create
var name;

// execute
name = 'Yumy';
console.log(name); // print Yumy

Wrong

console.log(name); // print undefined
var name = 'Jane';

// --- Equal to ---

// create
var name;

// execute
console.log(name); // print undefined,pois o valor ainda não foi atribuído, apenas o undefined padrão é retornado
name = 'Pitt';

2. What's name printed ?

whoseName();

function whoseName() {
if (name) {
name = 'Nini';
}
}

var name = 'Pitt';
console.log(name);

Answer

// create
function whoseName() {
if (name) {
name = 'Nini';
}
}
var name;

// execute
whoseName();
name = 'Pitt';
console.log(name); // print Pitt

name em whoseName() recebe undefined, portanto a condição não é atendida.

Porém, como há outra atribuição abaixo da declaração de função, mesmo que a condição dentro da function fosse atendida, o resultado final séria a impressão de Pitt.


3. Declaração de função vs Declaração de variável: Prioridade do Hoisting

Questão: Função e variável com o mesmo nome

Determine o resultado de saída do seguinte código:

console.log(foo);
var foo = '1';
function foo() {}

Resposta incorreta (equívoco comum)

Muitas pessoas pensam que:

  • A saída é undefined (achando que var é elevado primeiro)
  • A saída é '1' (achando que a atribuição tem impacto)
  • Ocorre um erro (achando que nomes iguais causam conflito)

Saída real

[Function: foo]

Por quê?

Esta questão examina as regras de prioridade do Hoisting:

Prioridade do Hoisting: Declaração de função > Declaração de variável

// Código original
console.log(foo);
var foo = '1';
function foo() {}

// Equivalente a (após Hoisting)
// Fase 1: Fase de criação (Hoisting)
function foo() {} // 1. Declaração de função elevada primeiro
var foo; // 2. Declaração de variável elevada (mas não sobrescreve a função existente)

// Fase 2: Fase de execução
console.log(foo); // Neste momento foo é uma função, saída [Function: foo]
foo = '1'; // 3. Atribuição de variável (sobrescreve a função)

Conceitos-chave

1. Declarações de função são completamente elevadas

console.log(myFunc); // [Function: myFunc]

function myFunc() {
return 'Hello';
}

2. Declarações de variável com var só elevam a declaração, não a atribuição

console.log(myVar); // undefined

var myVar = 'Hello';

3. Quando declaração de função e declaração de variável têm o mesmo nome

// Ordem após a elevação
function foo() {} // Função elevada primeiro é atribuída
var foo; // Declaração de variável elevada, mas não sobrescreve a função existente

// Portanto foo é uma função
console.log(foo); // [Function: foo]

Fluxo de execução completo

// Código original
console.log(foo); // ?
var foo = '1';
function foo() {}
console.log(foo); // ?

// ======== Equivalente a ========

// Fase de criação (Hoisting)
function foo() {} // 1. Declaração de função elevada (elevação completa, incluindo o corpo da função)
var foo; // 2. Declaração de variável elevada (mas não sobrescreve foo, pois já é uma função)

// Fase de execução
console.log(foo); // [Function: foo] - foo é uma função
foo = '1'; // 3. Atribuição de variável (somente agora sobrescreve a função)
console.log(foo); // '1' - foo se torna uma string

Exercícios avançados

Exercício A: Influência da ordem

console.log(foo); // ?
function foo() {}
var foo = '1';
console.log(foo); // ?

Resposta:

[Function: foo] // Primeira saída
'1' // Segunda saída

Razão: A ordem do código não afeta o resultado do Hoisting. A prioridade de elevação continua sendo: função > variável.

Exercício B: Múltiplas funções com o mesmo nome

console.log(foo); // ?

function foo() {
return 1;
}

var foo = '1';

function foo() {
return 2;
}

console.log(foo); // ?

Resposta:

[Function: foo] { return 2; } // Primeira saída (a função posterior sobrescreve a anterior)
'1' // Segunda saída (atribuição de variável sobrescreve a função)

Razão:

// Após a elevação
function foo() {
return 1;
} // Primeira função

function foo() {
return 2;
} // Segunda função sobrescreve a primeira

var foo; // Declaração de variável (não sobrescreve a função)

console.log(foo); // [Function: foo] { return 2; }
foo = '1'; // Atribuição de variável (sobrescreve a função)
console.log(foo); // '1'

Exercício C: Expressão de função vs Declaração de função

console.log(foo); // ?
console.log(bar); // ?

var foo = function () {
return 1;
};

function bar() {
return 2;
}

Resposta:

undefined; // foo é undefined
[Function: bar] // bar é uma função

Razão:

// Após a elevação
var foo; // Declaração de variável elevada (expressão de função só eleva o nome da variável)
function bar() {
return 2;
} // Declaração de função elevada completamente

console.log(foo); // undefined
console.log(bar); // [Function: bar]

foo = function () {
return 1;
}; // Atribuição da expressão de função

Diferença fundamental:

  • Declaração de função: function foo() {} → elevada completamente (incluindo o corpo da função)
  • Expressão de função: var foo = function() {} → apenas o nome da variável é elevado, o corpo da função não

let/const não têm esse problema

// var tem problemas de elevação
console.log(foo); // undefined
var foo = '1';

// let/const têm Zona Morta Temporal (TDZ)
console.log(bar); // ReferenceError: Cannot access 'bar' before initialization
let bar = '1';

// let/const com mesmo nome de função causa erro
function baz() {} // SyntaxError: Identifier 'baz' has already been declared
let baz = '1';

Resumo da prioridade do Hoisting

Prioridade do Hoisting (da mais alta para a mais baixa):

1. Declaração de função (Function Declaration)
├─ function foo() {} ✅ elevação completa
└─ prioridade mais alta

2. Declaração de variável (Variable Declaration)
├─ var foo ⚠️ apenas a declaração é elevada, não a atribuição
└─ não sobrescreve funções existentes

3. Atribuição de variável (Variable Assignment)
├─ foo = '1' ✅ sobrescreve a função
└─ ocorre na fase de execução

4. Expressão de função (Function Expression)
├─ var foo = function() {} ⚠️ tratada como atribuição de variável
└─ apenas o nome da variável é elevado, não o corpo da função

Pontos-chave em entrevistas

Ao responder este tipo de pergunta, é recomendado:

  1. Explicar o mecanismo do Hoisting: Dividido em fase de criação e fase de execução
  2. Enfatizar a prioridade: Declaração de função > Declaração de variável
  3. Desenhar o código após o Hoisting: Mostrar ao entrevistador sua compreensão
  4. Mencionar as melhores práticas: Usar let/const para evitar problemas de Hoisting com var

Exemplo de resposta em entrevista:

"Esta questão examina a prioridade do Hoisting. Em JavaScript, a declaração de função tem prioridade de elevação maior que a declaração de variável.

O processo de execução se divide em duas fases:

  1. Fase de criação: function foo() {} é completamente elevada ao topo, depois a declaração var foo é elevada, mas não sobrescreve a função existente.
  2. Fase de execução: Em console.log(foo), foo é uma função neste momento, então [Function: foo] é exibido. Depois, foo = '1' sobrescreve foo com uma string.

A melhor prática é usar let/const no lugar de var, e colocar as declarações de função no topo para evitar esse tipo de confusão."


Tópicos relacionados