Saspi 4

Ult Combo's Front-end/JS mini-course

Funções

O que são funções em JS?


  • Objetos.
  • Cidadãos de primeira classe.
  • Criam um novo escopo.

Cidadão de primeira classe


É um objeto cujo qual suporta todas operações disponíveis para outros objetos. Ele pode ser:

  • Passado como parâmetro (callbacks)
  • Retornado de uma sub-rotina (factory)
  • Atribuído a uma variável (FE)

Sintaxe


Function Declaration (FD)

function Identifier ( FormalParameterList(opt) ) { FunctionBody }

Function Expression (FE)

function Identifier(opt) ( FormalParameterList(opt) ) { FunctionBody }

Características

Function Declaration (FD)

  • Definida como um Statement isolado.
  • Definida durante o parse-time.
  • Acessível em todo o seu escopo pai.
  • Não pode ser imediatamente executada.

Function Expression (FE)

  • Definida como parte de uma Expressão maior.
  • Definida durante o run-time.
  • Acessível após sua definição.
  • Pode ser imediatamente executada.

Expressão

É uma combinação de explícitos valores, constantes, variáveis, operadores e funções que são interpretados de acordo com as regras de precedência e associação de uma linguagem de programação, que computa e então retorna um outro valor.

Exemplos

// Expressão aritmética
1 + 1 * 2;
// Expressões de atribuição
significadoDaVida = origemDoUniverso = 42;

Exemplo


//FD - Statement obrigatoriamente começa com a keyword "function"
function a(){}

//FE - parte de uma expressão de atribuição var b = function(){};

(II) A|N FE


Function Expression
Anonymous / Named

Immediately-Invoked

NFE

Diferentemente das FDs, o identificador (propriedade name) de uma FE só referencia a função dentro de seu próprio escopo.
//NFE - Named Function Expression
var funcRef = function identificador(naoRecursar) {
    console.log('fui chamado com sucesso.');
    if (naoRecursar) return;
    identificador(true); //válido
    funcRef(true); //válido
};
funcRef(); //válido
console.log( funcRef.name ); //"identificador"
identificador(); //erro
Nota: IE<9 possui um bug devido ao qual interpreta NFEs duplamente como FE e FD, criando dois objetos de função distintos e, devido a isto, o identificador da NFE vaza para o escopo pai.

IIFE

Immediately-Invoked Function Expressions, como o nome sugere, são executadas imediatamente após sua definição.
var retorno = (function() {
    return 42;
}());
console.log( retorno ); //42
Ambas sintaxes são perfeitamente válidas:
(function(){}());
(function(){})();
Douglas Crockford recomenda a primeira forma, pois com os parênteses invocativos "grudados" à função a relação entre estes torna-se perfeitamente clara.
É uma questão principalmente de preferência.

Por que parênteses na volta?


Um Statement que começa com a keyword function  é interpretado como FD.

function(){} //erro - FD necessita de um identifier
function funcName(){}() //erro - FD não pode ser imediatamente invocada

Uma forma fácil de torná-la uma FE é combiná-la com um operador, e o parêntese é um operador de agrupamento.
//function dentro de uma expressão de agrupamento - FE
(function(){}());

Por que parênteses na volta?


Também é possível utilizar outros operadores:
!function(){}();
+function(){}();
~function(){}();
//etc.
No entanto, estes afetarão o valor retornado da IIFE, obviamente.

Por que parênteses na volta?


E finalmente, uma IIFE que faz parte de uma expressão de atribuição naturalmente não necessita de parênteses na volta, mas isto é uma boa prática para diferenciar se é a função que está sendo atribuída ou o seu retorno.
var foo = function() { //parece que estamos atribuindo uma FE a foo
    //muitos linhas e scrolls depois...
}(); //SURPRISE MOTHERFUCKER - É UMA IIFE
A leitura do código torna-se mais clara seguindo a convenção:
var foo = (function() { //function entre parenteses? Deve ser uma IIFE
    //...
}());

FD vs FE


fd(); //válido
function fd(){}

fe(); //erro
var fe = function(){};

FD vs FE

if (true) {
    function fd(){ return 1; }
} else {
    function fd(){ return 2; }
}
console.log( fd() );
FDs dentro de blocos são, pela especificação atual, inválidos. Porém, a maioria dos browsers tenta "concertar" isto de alguma forma, logo o resultado varia de browser para browser.

var fe;
if (true) {
    fe = function(){ return 1; }
} else {
    fe = function(){ return 2; }
}
console.log( fe() );
Correto e cross-browser.

Aplicações práticas


//IINFE
(function timerContador(i) {
    if (i <= 10) {
        console.log(i);
        setTimeout(function() {
            timerContador(i + 1);
        }, 500);
    }
}(1));

Aplicações práticas


var estouPoluindoEscopoGlobal = true;
function maisPoluicaoGlobal() {}

(function() {
    var estouPoluindoEscopoGlobal = false;
    function existoSomenteDentroDestaIIFE() {}
}());

Aplicações práticas


for (var i = 0; i < 3; i++) {
    (function(i) {
        setTimeout(function() {
            console.log(i);
        }, 1);
    }(i));
}

Ult JS #2 - Funções

By Fabrício Matté

Ult JS #2 - Funções

  • 1,202