Funções como Objetos em JavaScript

Funções como Objetos em JavaScript

Neste artigo você terá uma visão geral sobre objetos e a palavra chave this em JavaScript. A partir deste overview, faremos um mergulho mais aprofundado em um tipo especial de objeto: as funções.

Mesmo que você esteja começando em JavaScript, já deve ter se deparado com funções e objetos. As funções são blocos de código reutilizáveis que automatizam tarefas como, por exemplo, processar dados. Objetos são abstrações de entidades que precisam ser representadas no software. Como assim? Objeto é uma representação de algo que está inserido em uma aplicação. Em um videogame de corrida, por exemplo, o carro pode ser representado por um objeto com seus atributos (características do objeto, como a cor e o modelo) e métodos (ações que o objeto pode realizar, como acelerar ou frear). Mas você sabia que em JavaScript funções são objetos?

Para tentar ajudar no entendimento, vamos imaginar dois objetos hipotéticos que poderiam existir em uma aplicação de compra e venda de produtos usados.

Objetos

Considere os objetos "cliente" e "vendedor'' abaixo. A partir deles, vamos discutir um pouco sobre como acessar atributos e como funcionam os métodos dos objetos

const cliente = {
    id: 12345,
    nome: "Gastão Pão Duro",
    comprasRealizadas: [],
    buscarNome: function(){
        const [primeiroNome, , ] = this.nome.split(' ');
        console.log(`Sr ${primeiroNome}`);
    },
    comprar: function (produto) {
        this.comprasRealizadas.push(produto);
        console.log(`${produto} comprado com sucesso!`);
    }
}
const vendedor = {
    id: 54321,
    nome: "Armando Schemma",
    estoqueProdutos: ["smartphone", "notebook"],
    vender: function (produto){
        const produtos = this.estoqueProdutos;
        const disponivel = produtos.find(item => item === produto)
        if (disponivel) {
            this.produtos = this.produtos.filter(item => item != disponivel);
            console.log("venda realizada com sucesso");
        } else {
            console.log(`O estoque de ${produto} está vazio`);
        }
    }
}

Atributos

Os atributos encontrados no objeto “cliente” são: “id”, “nome”, e “comprasRealizadas”. Já no objeto “vendedor”, temos os atributos “id”, “nome” e “estoqueProdutos”. Perceba que atributos representam dados ou coleções de dados relativos ao objeto que os possuem. Para acessar os atributos de um objeto, podemos utilizar uma das notações abaixo:

const nomeDoCliente = cliente.nome;
const estoque = vendedor['produtos'];
 
console.log(nomeDoCliente); // Gastão Pão Duro
console.log(estoque); // [ "smartphone", "notebook" ]

Métodos

Os métodos representam ações cuja responsabilidade é atribuída ao objeto que os possuem. O objeto “cliente” possui os métodos “comprar” e “buscarNome”, enquanto o objeto “vendedor” possui o método “vender”.

cliente.comprar("smartphone"); // smartphone comprado com sucesso
vendedor.vender("smartphone"); // venda realizada com sucesso

Perceba que o método “comprar” inclui o produto comprado na lista de compras que consta no atributo “comprasRealizadas”, assim como o método vender atualiza o estoque de produtos à venda que está contido no atributo “estoqueProdutos”:

console.log(vendedor.produtos); // [ 'notebook' ]
console.log(cliente.compras); // [ 'smartphone' ]

What’s this ?

Conforme se observa neste artigo da W3Schools, this é uma palavra-chave em JavaScript que aponta para um objeto. Que objeto? Depende. Quando invocada sozinha, por exemplo, this aponta para o Objeto Global do JavaScript. Quando invocada em uma função e o JavaScript está sendo executado em “strict mode”, this aponta para undefined. No contexto do nosso exemplo, por ter sido chamada dentro de um objeto, this aponta para o objeto que o contém. Em outras palavras, no objeto “cliente”, this aponta para o próprio objeto “cliente”, enquanto que no objeto “vendedor”, this aponta para o objeto “vendedor”.

Funções como Objetos em JavaScript

Com esta introdução básica sobre objetos, podemos nos aprofundar no objetivo deste artigo, que é a compreensão de que funções, em JavaScript, são objetos e como tal possui atributos e métodos. Considerando que métodos de um objeto também são funções, podemos utilizá-los para demonstrar os métodos e atributos do objeto função.

Atributos

Imagine que o comprador precise consultar o nome do vendedor e só possua o "id" do mesmo. Na vida real, seria necessário realizar uma consulta ao banco de dados para obter esta informação. Para simular isto, vamos imaginar um array de vendedores obtido por um banco de dados e uma função que retorna o nome do vendedor, caso o "id" corresponda ao informado.

const vendedores = [
    {
        id: 54321,
        nome: "Armando Schemma",
        estoqueProdutos: ["smartphone", "notebook"],
        vender: ()=>{}
    },
    {
        id: 54322,
        nome: "José Schemma",
        estoqueProdutos: ["martelo", "parafuso"],
        vender: ()=>{}
    },
    {
        id: 54323,
        nome: "Maria Schemma",
        estoqueProdutos: ["livro", "caderno"],
        vender: ()=>{}
    }
]
function identificarVendedor(vendedores, id){
    const vendedor = vendedores.find(vendedor => vendedor.id === id);
    console.log(vendedor.nome);
}
identificarVendedor(vendedores, 54321); // Armando Schemma

As funções possuem o atributo length, que retorna o número de argumentos passados como parâmetros na declaração da função.

console.log(identificarVendedor.length); // 2
console.log(vendedor.vender.length); // 1

Outro exemplo é o atributo name, que retorna o nome da função.

console.log(cliente.comprar.name); // comprar

Métodos

Call e Apply

Os métodos call e apply realizam a mesma tarefa. Eles executam a função que o invoca, fazendo com que this aponte para um objeto especificado. A diferença entre eles é que os parâmetros recebidos pelo método call são passados individualmente, enquanto o método apply os recebe dentro de um array. Aplicando os métodos call e apply da função “trocar” e especificando o objeto “vendedor” como parâmetro, obtemos:

trocar.call(vendedor, "notebook"); // Sua troca foi realizada com sucesso.
trocar.apply(vendedor, ["smartphone"]); //Estoque insuficiente. Seu dinheiro será devolvido.

Bind

Imagine agora que o smartphone comprado pelo cliente apresente defeito. Nossa aplicação precisa possibilitar que o produto seja trocado. Para isto, vamos criar a função trocar.

function trocar (produto){
    const estoqueProdutos = this.estoqueProdutos;
    const disponivel = produtos.find(item => item === produto);
    if(disponivel){
        console.log('Sua troca foi realizada com sucesso.');
    } else {
        console.log('Estoque insuficiente. Seu dinheiro será devolvido.');
    }
}
trocar("smartphone"); // TypeError: Cannot read property 'find' of undefined

Quando executamos a função, o JavaScript, meio nervoso, informa que não consegue ler a propriedade “find” de "undefined". Isso acontece porque atribuímos à variável “produtos” o valor de this.produtos e, como a função “trocar” não está vinculada a nenhum objeto, this está apontando para o valor "undefined". Para resolver este problema, as funções possuem o método bind. Quando chamado, bind retorna uma nova função, com o mesmo corpo da original, mas com a palavra-chave this apontando para o objeto que for especificado na chamada do método.

const trocarVenda = trocar.bind(vendedor);
trocarVenda("smartphone"); //Estoque insuficiente. Seu dinheiro será devolvido.

Sim! Por mais esquisito que possa parecer, funções podem retornar uma nova função e é exatamente o que o método bind faz. Neste exemplo, a função “trocarVenda” poderá ser invocada sempre que necessário, sem a necessidade de alterar o objeto “vendedor”.

Outra possibilidade de uso do método bind é a aplicação parcial. Esta técnica permite criar uma nova função, com o mesmo corpo da original, mas com parâmetros padrão especificados. Imagine que precisamos identificar vendedores residentes na Bahia com frequência. Poderíamos pesquisar no banco de dados esta lista de vendedores, salvar na variável "vendedoresBaianos" e fazer o seguinte:

const vendedoresBaianos = [
    {
        id: 54322,
        nome: "José Schemma",
        produtos: ["martelo", "parafuso"],
        vender: ()=>{}
    },
    {
        id: 54323,
        nome: "Maria Schemma",
        produtos: ["livro", "caderno"],
        vender: ()=>{}
    }
];
 
const identificarVendedorBaiano = identificarVendedor.bind(null, vendedoresBaianos);
 
identificarVendedorBaiano(54323) //Maria Schemma

Nesse caso, ao valor de this foi atribuído null e o parâmetro “vendedoresBaianos” foi estabelecido como padrão. Sempre que “identificarVendedoresBaianos” foi chamada, a pesquisa será realizada no array “vendedoresBaianos”, restando passar o “id” como parâmetro.

Com os métodos call, apply e bind, é possível invocar o método de um objeto em outro. O objeto “cliente” possui o método “buscarNome”, que imprime no console o primeiro nome do cliente. Esse método não está disponível no objeto “vendedor”.

cliente.buscarNome(); // Sr Gastão
cliente.buscarNome.call(vendedor); // Sr Armando
cliente.buscarNome.apply(vendedor); // Sr Armando
const buscarPrimeiroNome = cliente.buscarNome.bind(vendedor);
buscarPrimeiroNome(); //Sr Armando

Neste artigo, apresentamos alguns conceitos básicos concernentes à estrutura dos objetos e sobre como acessar seus atributos e invocar seus métodos. Falamos um pouco a respeito de funções, esclarecendo que, no fim das contas, em JavaScript, funções são objetos que possuem métodos e atributos próprios, do mesmo jeito que os arrays, por exemplo. Por fim, como exemplos de atributos de função, apresentamos name e length. Descrevemos, também, um pouco do funcionamento dos métodos bind, call e apply.

Obrigado pela atenção. Se você achou o conteúdo interessante, por favor, compartilhe com seus amigos e deixe seu comentário. Até a próxima!

🛑
As opiniões e comentários expressos neste artigo são de propriedade exclusiva de seu autor e não representam necessariamente o ponto de vista da Revelo. A Revelo Content Network acolhe todas as raças, etnias, nacionalidades, credos, gêneros, orientações, pontos de vista e ideologias, desde que promovam diversidade, equidade, inclusão e crescimento na carreira dos profissionais de tecnologia.