Funções JavaScript - Declaração e Escopos

Posted by Raphael Lima on 06-11-2016

Este é o terceiro post da série sobre Funções JavaScript. Você pode encontrar os anteriores nos links abaixo: Funções JavaScript e A importância da programação funcional em JS

Se tem algo que me deixou confuso durante o aprendizado de JavaScript e acredito que muitos também ficaram, foram os conceitos sobre funções, escopos e etc. Neste post veremos como são declaradas as funções e também entenderemos o que é e como funciona seu escopo.

Declaração de Funções

Declaração de funções em JS é um tanto quanto diferente, principalmente quando se vem de outras linguagens tais como, Java, C# e etc. A liberdade de declarar funções em qualquer parte do código, passa-las como argumento para outras funções e até mesmo atribui-las à variáveis ou propriedade de objetos me deixou muito confuso no início. Bom, primeiro de tudo, funções JavaScript são declaradas utilizando literais de funções, ou seja, quando uma função é declarada é criado um valor de função. Funções são objetos de primeira classe, ou seja, valores que podem ser utilizados como qualquer outro, assim como Strings e Numbers.

Você deve estar se perguntando: “O que é esse tal de literal de função?”. Vamos entender o que é isso… Os literais de função são formados por quatro partes, que são:

function [name] ([arg1], [arg2]) {

}
  • A palavra-chave function. Que deixa explícita a declaração da função.
  • O nome da função. Note que na declaração de função acima, o nome da função está entre [], ou seja, sua declaração é opcional (desde que esta seja atribuída a uma variável ou seja uma função de callback), podemos declarar uma função com a seguinte assinatura function () {}. Caso o nome da função seja declarado, não pode ser declarado com palavras reservadas da linguagem.
  • Os parâmetros. Entre parênteses podem ser passados um ou mais parâmetros separados por vígulas ou até mesmo nenhum parâmetro. Embora os parâmetros sejam opcionais, os parênteses são obrigatórios e devem estar presentes em todas as declarações de funções.
  • O corpo da função. Ou seja, uma série de instruções no bloco delimitado por {}.

Funções JS possuem uma propriedade name que armazena o nome da função que pode ser acessado da seguinte forma: function.name. Funções anônimas, ou expressão de função, também possuem a propriedade name, porém, definida como uma String vazia.

Quando declaramos uma função nomeada, seu nome é válido em todo o escopo do qual a função foi declarada. Vejamos alguns exemplos de códigos de declaração de funções:

function funcao () {
  //Função nomeada, disponível no escopo atual e incluída implicitamente
  //como propriedade do objeto window.
}

var outra-funcao = function () {
  //Função anônima atribuída à variável outra-funcao, propriedade de window e com
  //propriedade name vazia.
}

function externa () {
  function interna (){
    //Função nomeada, porém, acessível apenas dentro do escopo da função externa.
  }
}

A 3º função declarada com uma função interna deixa a seguinte dúvida: “Em qual escopo a função declarada fica disponível?”. Vejamos o próximo tópico.

Escopo de Funções

Em JavaScript, diferente de outras linguagens, os escopos são definidos por funções e não por blocos, ou seja, mesmo que o bloco tenha terminado o escopo da declaração não é encerrado. Por exemplo:

if (condicao) {
  var variavel = 123;
}
console.log(variavel); //Imprime 123

Embora a instrução para imprimir o valor da varíavel no console esteja fora da função, o código funciona corretamente devido o JavaScript não encerrar escopos ao final de um bloco e a variável ter sido declarada usando a keyword var. Na versão atual do JS (ES6), foram introduzidos dois tipos adicionais let e const (detalharemos o ES6 em posts futuros). Se o fizesse, a variavel ‘variavel’ seria acessível apenas dentro do bloco ‘if’ e seria impresso na console o valor undefined.
Isso parece simples, mas devemos nos atentar para algumas particularidades como:

  • Variáveis declaradas em funções ficam disponiveis no escopo até o final de sua função, independentemente de blocos aninhados.
  • Funções nomeadas pertencem ao escopo dentro de toda a função, sem considerar o aninhamento de blocos. Também conhecido com hoisting.
  • O contexto global atua como uma função que abrange todo o código.

Para entendermos melhor os escopos, vejamos o código abaixo:

//Inicio do escopo da função externa()
function externa () {
  //Início do escopo da função interna()

  var x = 10; //Inicio do escopo de X

  function interna () {
    var y = 20; //Inicio do escopo de Y
    if (x == 10) {
      var z = 30; //Inicio do escopo de Z
    }
  }

  //Fim do escopo de X, Y e Z
} //Fim do escopo da função interna()
//Fim do escopo da função externa()
  • Com esse código podemos observar que a função externa() pertence ao escopo global.
  • O escopo de ‘X’ inicia-se no momento de sua declaração e termina no fim da função externa().
  • A função interna(), embora declarada após a declaração da variável ‘x’, o seu escopo inicia-se logo em seguida a abertura do bloco da função externa e pode ser invocada mesmo antes de sua declaração.
  • O escopo de ‘Y’ e ‘Z’ inicia-se no momento de sua declaração e termina no fim da função externa().

Bom, por hoje é só. Deixem seus comentários e até o próximo!