Funções
Uma função é um bloco de código, com nome, que executa uma tarefa e que pode ou não retornar um resultado.
Declaração de funções
Uma função é declarada usando a seguinte sintaxe:
function nome(parâmetro, parâmetro2, parâmetro3,…) {
// corpo da função
}
Os parâmetros de uma função são opcionais podendo a função não ter qualquer parâmetro.
O corpo da função é o conjunto de instruções entre as chavetas da função e que são executadas quando a função é chamada.
Uma função pode ou não retorna um valor. Se retornar, fá-lo com a instrução return que, por sua vez, termina a execução da função.
Exemplo:
function somar(nr1, nr2){
let resultado = nr1 + nr2;
console.log( resultado );
}
Hoisting de funções
O Javascript move automaticamente as declarações de funções para o topo do código. Chama-se a isto hoisting de funções. Isto permite chamar uma função antes de ela ser declarada:
add();
function add(){
console.log(7 + 4);
}
Parâmetros de uma função
Podemos invocar uma função com um número de argumentos inferior ao número de parâmetros. Nesse caso, o valor dos argumentos em falta será undefined:
function soma(a, b, c){
//função com 3 parâmetros
console.log(a, b, c);
}
soma(3, 4); //função chamada só com 2 argumentos
O output deste código será: 3
4
Undefined
Parâmetros com valor de defeito
As funções podem ter parâmetros com valores de defeito sendo que, nesse caso, se não fôr passado nenhum valor para esse parâmetro, ele assume o valor de defeito:
function soma(x, y, z=4){
return x + y + z;
}
let r = soma(2, 3); console.log(r);
Neste exemplo o parâmetro z assume o valor de defeito, 4, uma vez que na chamada da função não foi passado qualquer argumento para esse parâmetro.
O output do exemplo é :
9
Chamada de uma função
Uma função é chamada pelo seu nome seguido de (), com ou sem argumentos no interior dos parenteses e, desta forma, o código do corpo da função é executado:
somar(); //chama a função somar().
Se a função retornar um valor, ele é recebido quando se chama a função e, normalmente, é guardado numa variável:
//chama a função somar() e guarda o valor por ela retornado na variável resultado:
let resultado = soma();
Funções que retornam um valor
Uma função pode ou não retornar um valor. Se retornar, fá-lo com a instrução return.
A instrução return faz duas coisas :
- Pára a execução da função.
- Retorna um valor para a instrução que invocou a função.
Exemplo:
function somar(nr1, nr2){
let resultado;
resultado = nr1 + nr2;
return resultado;
}
let res = somar(4, 3); //chama a função somar()e recebe o resultado
No exemplo acima, a função somar() é invocada, a instrução return devolve o resultado e este é recebido na instrução que invocou a função e guardado na variável res.
Funções e this
A palavra reservada this refere-se ao objeto a que ela pertence.
Numa função, a palavra reservada this refere-se ao objeto global que no caso de um browser é o objeto window:
function f(){
alert(this);
}
f();
O output deste Código será a seguinte janela de alerta:

No caso de uma função que seja um método de um objeto, this refere-se a esse objeto:
var pessoa = {
nome: 'Xico',
morada: 'Lisboa',
estuda: function(){
alert(this.nome); //this refere-se ao objeto pessoa
}
}
pessoa.estuda();
O output deste código será a seguinte janela de alerta:

Funções anónimas
Quando declaramos uma função, atribuímos-lhe um nome que usamos para a chamar.
No entanto, em certas situações, podemos criar uma função que não tenha nome, ou seja, uma função anónima.
Funções anónimas atribuídas a uma variável
Podemos criar uma função anónima e atribui-la a uma variável como se de uma expressão se tratasse. A função é invocada através da variável:
let s = function (nr1, nr2){
let resultado;
resultado = nr1 + nr2;
return resultado;
}
//chamamos a função usando o nome da variável a que a função foi atribuída
let res = s(4, 3);
console.log( res );
No exemplo acima, atribuímos uma função anónima à variável s e invocámos a função através da variável.
Funções anónimas como argumentos de outras funções
Podemos passar uma função anónima como argumento de outra função:
function add(n1, n2, s){
return s(n1, n2); }
let res = add(4, 3, function (nr1, nr2){
let resultado;
resultado = nr1 + nr2;
return resultado;
});
console.log( "Resultado: " + res );
Neste exemplo o parâmetro s da função add(), recebe uma função anónima que depois será invocada na função add() sendo o resultado devolvido.
Funções que se auto invocam
São funções que se auto invocam e executam automáticamente sem que tenha de haver uma invocação expressa.
NOTA: São conhecidas pela sigla IIFEs (Immediatly Invoked Funtions Expressions).
Exemplo:
(function () {
alert("Olá !!");
})();
O output deste código será a seguinte janela de alerta:

Funções de seta (arrow functions)
A partir do ES6, surgiu uma nova sintaxe, mais curta, para a escrita de funções: são as funções de arrow (arrow functions).
A sua sintaxe é a seguinte:
(parâmetros) => { instruções }
Nesta nova sintaxe não são necessárias as palavras reservadas function e return. Se as instruções se resumirem a uma única instrução ou expressão, as chavetas e a palavra reservada return podem ser omitidas.
Consideremos a seguinte função:
// função anónima na sintaxe normal
let f = function(n1, n2) {
return n1 + n2;
}
let res = f(4, 3);
A função anónima acima pode ser codificada na forma de função de seta:
// função de arrow
const f = (n1, n2) => n1 + n2;
let res = f(4, 3);
De notar a ausência da palavra reservada function, das chavetas e da palavra reservada return.
Se só houver um parâmetro, podemos eliminar os parenteses curvos:
const r = n => n * 2;
let res = r(3);
As funções de seta devem ser declaradas antes de serem usadas.
Closures
Uma closure é uma função dentro de outra função que tem acesso às variáveis locais da função externa (mesmo quando a função externa se extingue):
function externa() {
let x = 2;
return function interna() {
return ++x;
}
}
let f = externa();
console.log( f() );
console.log( f() );
A função interna() está dentro do escopo da função externa(), logo tem acesso às suas variáveis locais incluindo os parâmetros se os houver.
A função externa() declara a variável x e, no final, retorna a definição da função interna().
Quando chamamos a função externa() e a executamos, estamos de facto a executar a função interna(), que continua a ter acesso à variável local x, da função externa() mesmo quando esta se extingue.
Para comprovarmos isso, as duas instruções
console.log( f() );
console.log( f() );
têm como output :
3
4
Ou seja, a variável x, teve um comportamento semelhante a uma variável estática.
Funções construtoras
Um função pode ser um construtor de um objeto. Neste caso, o nome da função deve ter a primeira letra em maiúsculas.
Para criarmos um objeto, chamamos a função com a palavra reservada new.
Exemplo:
function Aluno(nrAluno, nome, turma){
this.nrAluno = nrAluno;
this.nome = nome;
this.turma = turma;
}
let aluno = new Aluno(1, 'Xico', 'Lisboa');
Na prática, as funções construtores atuam como classes.
this
Quando criamos um objeto usando uma função construtora a palavra reservada this refere-se ao próprio objeto.
Prototypes em funções construtoras
Quando uma função é criada, o Javascript adiciona uma propriedade prototype à função. Esta propriedade é um objeto (objeto prototype) que tem uma propriedade constructor que aponta à própria função.
Considere-se a seguinte função construtora:
function Cat(nome, idade) {
this.nome = nome;
this.idade = idade;
}
Para acedermos à propriedade prototype da função fazemos:
console.log(Cat.prototype);
o output é:

Podemos ver duas propriedades no objeto prototype da função Cat(): constructor e proto.
A propriedade constructor aponta à própria função Cat().
A propriedade proto tem a ver com herança e não é discutida aqui.
Agora criemos um objeto com a função construtora Cat():
let c1 = new Cat('xico', 4);
façamos
console.log(c1);
o output será:

Ou seja, veremos o objeto c1 com as sua propriedades e uma propriedade proto.
Esta propriedade proto tem um objeto que aponta para Cat.prototype.
Tendo a propriedade Cat.prototype um objeto, podemos criar dinamicamente propriedades e métodos nesse objeto:
Cat.prototype.raca = 'siames';
Adicionámos a propriedade raca ao objeto prototype de Cat().
Criemos dois objetos c1 e c2:
let c1 = new Cat('messi', 3);
let c2 = new Cat('ronaldo', 4);
Se visualizarmos os dois objetos com console.log() teremos o seguinte output:

Ambos “herdaram” a propriedade raca do objeto Cat.prototype !!!
Se fizermos
console.log(c1.raca) ou console.log(c2.raca), teremos em ambos o output siames
Isto porque o Javascript vai procurar a propriedade raca ao objeto e, se não a encontrar, vai procura-la à propriedade proto.
