Em algumas linguagens de programação, funções são como blocos de código que executam uma determinada tarefa e que podem ser reaproveitados. Em JavaScript, isso também é verdade, porém, as funções possuem um certo poder na linguagem que permite que existam algumas peculiaridades bem interessantes. Então, pega o café e bora começar!
DICA: acompanhe esse artigo com um console ou terminal aberto e vá testando os códigos para ficar mais divertido 😁
#1 Funções são valores
COMO ASSIM!? Em JavaScript funções também são valores?
Observe o exemplo:
function nomeQualquer () {
// código qualquer}
Isso é uma função em JavaScript, e talvez não pareça ser tão complicado, pois a maioria das linguagens possuem uma sintaxe semelhante na hora de declarar funções. Massss, você também pode fazer isso aqui:
let minhaFuncao = function () {
// código qualquer
}
Você acabou de declarar uma variável e atribuiu a ela uma função. Agora o mais legal, execute typeof minhaFunction
. BAM! A variável possui o tipo "function"
, logo, funções podem ser valores, assim como string
, number
, boolean
, etc. Isso não ocorre em outras linguagens, como Java, por exemplo.
#2 Arguments
Observe a seguinte função:
function soma (a, b) {
return a + b
}
Se você executar soma(2, 2)
o retorno será igual a 4, mas e se você passar mais argumentos, soma(2, 2, 3, 4, 5)
, consegue adivinhar o resultado?
Não, não será um erro, os argumentos extras serão simplesmente ignorados e o retorno continuará sendo 4.
Para acessar os argumentos extras você pode usar o objeto arguments, ele funciona como uma variável de escopo local dentro de todas as funções.
function mostraArgumentos (...arguments) {
console.log(arguments)
}
Ao invocar a função acima você poderá observar o seguinte resultado:
mostraArgumentos(2, 2, 3, 4, 5)
//output
[2, 2, 3, 4, 5]
Arguments
não é um Array
. Parece um Array, mas possui apenas a propriedade length
. E o interessante é que você pode referenciar os argumentos passados em uma função acessando pelo índice de cada um, começando pelo 0:
function soma (a, b) {
return arguments[0] + arguments[arguments.length - 1]
}
soma(2,3,4,5)
//output
7
No código acima, a função soma
foi reescrita e os argumentos foram acessados pelo objeto arguments. Observe que o retorno será a soma entre o primeiro argumento passado e o último.
#3 call() e apply()
Podemos invocar funções em JavaScript de várias formas diferentes:
— Diretamente, nomeDaFuncao()
; passando uma função como parâmetro de outra função, funcaoA(arg, funcaoB)
; ou retornando uma função de dentro de outra, funcaoA()()
.
Imagine que você tenha a função mostraIdade:
let mostraIdade = function () {
return this.idade;
};
Sabemos que this, nessa situação, refere-se ao escopo da função mostraIdade.
Agora vamos criar um objeto pessoa:
let pessoa = {
nome :'Maria',
idade : 30
};
Podemos acessar a propriedade idade do objeto pessoa com a função mostraIdade ? Sim! Basta utilizar:
mostraIdade.call(pessoa);
ou
mostraIdade.apply(pessoa);
call()
e apply()
são propriedades que permitem que você indique em qual escopo que a função deve ser executada. No exemplo acima, o escopo passado foi o objeto pessoa
, no qual a função mostraIdade
buscou o atributo idade
.
Mas e por que dois, se eles fazem exatamente a mesma coisa?
A única diferença entre call()
e apply()
está em como você passa os argumentos, além do argumento de escopo. Com call()
você pode passar um conjunto de argumentos nomeados, funcao(escopo, arg1, arg2)
. Com apply()
, funcao(escopo, [arg1, arg2, ...])
, os argumentos são passados como um único array ou um objeto semelhante a um array. Por exemplo, você pode usar um NodeList
ou um objeto com índices personalizados como { '0': 'laranja', '1': 'abacaxi' }
.
# 4 bind()
O conceito da propriedade bind()
talvez seja um dos mais intrigantes em JavaScript. Segundo a documentação oficial:
A função
bind()
cria uma nova função vinculada (bound function). Uma função vinculada é um objeto de função exótico (termo da ECMAScript 2015) que encapsula o objeto de função original. Chamar uma função vinculada geralmente resulta na execução de sua função encapsulada.
Confuso, né? Mas ele basicamente cria uma nova função que vincula uma função A a outra função B, e ainda vincula o contexto dessa outra função B. Vamos ao exemplo.
Você tem o seguinte objeto cachorro
:
let cachorro = {
som : "auau",
latir : function () {
return this.som
}
}
Note que, se você chamar cachorro.latir()
receberá a string "auau"
, isso porque o método latir
retorna o atributo som
. Mas agora vamos fazer alguns teste:
let funcaoLatir = cachorro.latir
No código acima, o valor da variável funcaoLatir
é o método latir
, certo?Então, isso significa que se invocarmos funcaoLatir()
vamos obter "auau"
, certo? Não!
O resultado será undefined
, mas por quê? Note que o método latir
possui a palavra-chave this
, isso significa que ele retorna a propriedade som
no contexto do objeto em que está inserido. A variável funcaoLatir
não possui o contexto do objeto cachorro
e muito menos um atributo som
, é apenas uma variável que armazena um método.
Para fazer com que funcaoLatir()
de fato retorne "auau"
temos que vincular seu contexto ao do objeto cachorro
e é aí que a propriedade bind()
entra:
let funcaoLatir = cachorro.latir.bind(cachorro)
Ficou um pouco esquisito, né? Vamos fazer um exemplo que pode ocorrer na vida real:
let botao = document.getElementById('meuBotao')// agora você que adiciona um evento a esse botãobotao.addEventListener('click', cachorro.latir)
Quando você clicar no botao
, o this
do método latir
não será no contexto do cachorro
, mas sim a janela do navegador em que o addEventListener
está sendo chamado. Para resolver isso, bastar chamar a propriedade bind()
e passar o contexto desejado:
botao.addEventListener('click', cachorro.latir.bind(cachorro))
Agora, o que foi passado ao addEventListener
não foi a janela do navegador, mas sim uma nova função que conecta cachorro.latir
ao contexto do objeto cachorro
. Bizarro, né? Mas é assim que o bind()
funciona, cria uma nova função que serve para conectar uma função a outra.
Se você for uma pessoa esperta, já deve ter testado o exemplo acima usando apply()
ou call()
e pensado: “por que existe o bind()
se eu usei o call()
e funcionou?”. E, realmente, funciona como o bind()
, mas diferente dele, call()
e apply()
executam a função vinculada imediatamente. Talvez, em algum momento da sua vida, você não vai querer que isso ocorra e vai usar o bind()
.
# 5 Function declaration x Function expression
Se você já criou uma função em JavaScript deve ter se deparado com essas duas formas aqui:
function declarationFun () {
//algum código aqui
}// invocando a função
declarationFun()
e
let expressionFun = function () {
// algum código aqui
}// invocando a função
expressionFun()
Com certeza, em algum momento da sua vida, já deve ter ouvido alguém falar “ah, não faz diferença a ordem em que você declara e executa funções”.
Será mesmo?
declarationFun()function declarationFun () {
//algum código aqui
}
Se você rodar o código acima ele será executado, e é por isso que existe esse mito de que “não importa a ordem de declaração das coisas” em JavaScript. Mas e se a gente executar isso aqui:
expressionFun()let expressionFun = function () {
// algum código aqui
}
Se você tentou aí, viu que o código gera um TypeError
. Afunção que você invocou não existe, logo, você precisa inverter a ordem e invocar a função após ela ser declarada, ou seja, a ordem, nesse caso, importa sim.
Mas por que com a
declarationFun()
funciona?
A function declaration é carregada sempre antes do código ser interpretado, é como se ela fosse mandada para o topo do código. Por isso, realmente não importa se você invoca antes ou depois da declaração.
Com a function expression ocorre de maneira diferente, ela é carregada durante a interpretação do código, portanto, você deve seguir a ordem de interpretação na hora de invocar funções criadas dessa forma.
Qual a melhor forma? Não existe melhor forma, o importante é saber que existem diferenças entre as duas formas, não apenas na declaração, mas também na hora de invocar as funções. E da próxima vez que alguém chegar com essa de ser tudo bagunçado com funções, você pode dizer “DEPENDE!” 😎.
Se você chegou até aqui, PARABÉNS! Agora você sabe algumas curiosidades e conceitos bem legais sobre funções em JavaScript! 👏👏👏👏👏
Não se esquece de continuar praticando e se achar outras coisas legais sobre funções comenta aqui!
Referências
- https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Guide/Fun%C3%A7%C3%B5es
- https://www.youtube.com/watch?v=OqR0hE-DQn4&t=
- https://www.youtube.com/watch?v=BMUiFMZr7vk&t=564s
- https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Function/Apply
- https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Function/call
- https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Functions/arguments