Como usar Classes no JavaScript

Classes são uma das principais características da orientação a objetos no JavaScript e servem modelos para criar objetos (instâncias) que compartilham propriedades e métodos. É uma maneira de escrever o código de uma forma mais organizada e reutilizável.

Antes do ECMAScript 2015 (ES6), o JavaScript não tinha suporte nativo para classes. Em vez disso, os desenvolvedores usavam funções construtoras e herança por protótipo para imitar a funcionalidade de classes. Com a introdução do ES6, o JavaScript agora oferece suporte nativo para classes.

Estrutura Básica

A sintaxe básica de uma classe no JavaScript é a seguinte:

class NomeDaClasse {
  constructor(propriedade1, propriedade2) {
    this.propriedade1 = propriedade1;
    this.propriedade2 = propriedade2;
  }
  metodo1() {
    // código do método 1
  }
  metodo2() {
    // código do método 2
  }
}

A classe tem um construtor, que é uma função especial chamada quando um novo objeto é criado a partir da classe. O construtor pode ter parâmetros para inicializar as propriedades do objeto. Além disso, uma classe pode ter métodos, que são funções que fazem alguma coisa com o objeto.

Para criar uma nova instância de uma classe, você usa a palavra-chave “new”:

const minhaInstancia = new NomeDaClasse(valor1, valor2);

A partir daí, você pode acessar as propriedades e métodos da instância como:

minhaInstancia.propriedade1;
minhaInstancia.metodo1();

Como era antes do ES6

Precisamos lembrar que o JavaScript é uma linguagem prototipada, então a palavra “class” é um facilitador e por baixo dos panos faz algo parecido com o seguinte código:

function NomeDaClasse(propriedade1, propriedade2) {
  this.propriedade1 = propriedade1;
  this.propriedade2 = propriedade2;

  this.metodo1 = function() {
    // código do método 1
  };

  this.metodo2 = function() {
    // código do método 2
  };
}

var objeto = new NomeDaClasse('valor1', 'valor2');

Herança

Além disso, você pode estender uma classe existente para criar uma nova classe com suas próprias propriedades e métodos adicionais. Isso é chamado de herança e é feito usando a palavra-chave extends.

O código abaixocria uma nova classe NomeDaClasseFilha que herda as propriedades e métodos da classe:

class NomeDaClasseFilha extends NomeDaClasse {
  constructor(propriedade1, propriedade2, propriedade3) {
    super(propriedade1, propriedade2);
    this.propriedade3 = propriedade3;
  }
  metodo3() {
    // código do método 3
  }
}

Conclusão

Isso ai, esse é o fundamento de utilização de classes no JavaScript. Se você teve duvida ou gostaria de um tutoria mais a fundo, prenda uma mensagem com a sua duvida e veremos o que podemos faze para te ajudar.

Estrutura de Dados e Classes JavaScript: Pilha

A pilha é uma estrutura de dados que implementa o conceito de último a entrar, primeiro a sair (LIFO (Last in First Out)), ou seja, o último elemento adicionado será o primeiro elemento a ser removido. Utilizaremos também, na nossa estrutura de dados, a palavra-chave class, que foi introduzida no ECMAScript 2015. Mas isso não significa que JavaScript é uma linguagem orientada e objetos. O JavaScript continua sendo orientado a protótipos, ou seja, o prototype continua agindo “por baixo dos panos”, e a palavra-chave class apenas “mascara” toda essa prototipagem, tornando o código mais simples de ser lido.

Abaixo temos uma imagem do funcionamento da pilha:

Então, para começarmos a implementar a pilha, precisamos construir a nossa classe Pilha e seu construtor. Vamos implementar também um contador, que será útil para nos mostrar a quantidade de itens na pilha. No nosso construtor, precisamos setar nossos valores do contador e do topo da pilha. Como ainda não há nada na pilha, o topo é null e o contador é 0:

class Pilha {
    constructor(top = null, count = 0){
        this.top = top;
        this.count = count;
    }

    GetContador(){
        return this.count;
    }
}

Nossa pilha terá os seguintes métodos:

  • Push;
  • Visualizar;
  • Remover;
  • MostrarTodos.

Push:

Para criar nosso método push, precisamos criar um nó. Cada nó precisa ter o dado e a referência para o próximo nó, que precisa ser null, pois cada vez que inserirmos algo na pilha, esse elemento inserido deverá ser o primeiro a ser removido. Então, cada nó inserido deve virar o topo da pilha.

Push(data){
    let no = {
       data: data,
       next: null
    };

    no.next = this.top;
    this.top = no;
    this.count++;
}

Visualizar:

Para visualizarmos o item que está no topo da pilha, precisamos nos certificar de que a pilha não está vazia. Caso a pilha não estiver vazia, retornaremos o dado que está no topo de nossa estrutura:

Visualizar(){
    if (this.top === null){
        return null;
    } else {
        return this.top.data;
    }
}

Remover:

Para remover o elemento do topo da pilha, precisamos entender que a pilha não poderá estar vazia. Caso ela não esteja vazia, removeremos o elemento do topo e diminuiremos o nosso contador em 1.

Remover(){
    if (this.top === null){
        return null;
    } else {
        let remover = this.top;
        this.top = this.top.next;

        if (this.count > 0){
            this.count--;
        }
        return remover.data;
    }
}

MostrarTodos:

Iremos adicionar os dados em um vetor, para então exibi-los. Precisamos ter certeza que nossa pilha não está vazia e, então, criamos um vetor, Utilizaremos um laço de repetição que irá inserir cada elemento de nossa pilha em sua respectiva posição:

MostrarTodos(){
    if (this.top === null){
        return null;
    } else {
        let arr = [];
        let current = this.top;

        for (let i = 0; i < this.count; i++){
            arr[i] = current.data;
            current = current.next;
        }
        return arr;
    }
}

E podemos testar a nossa estrutura de dados, instanciando uma nova pilha:

let pilha = new Pilha();
pilha.Push(1);
pilha.Push(2);
pilha.Push(3);
pilha.Push(4);
pilha.Push(5);
pilha.Push(6);
pilha.Push(7);
pilha.Remover();
console.log(pilha.Visualizar());
console.log(pilha.MostrarTodos());
console.log(pilha);

E a saída será:

Gostou deste artigo? Comente abaixo!

 

JavaScript OOP: Uma introdução parte 2

Como já vimos na parte 1 da nossa Introdução a OOP, O JavaScript possui agora a palavra Class, que “mascara” o prototype que continua acontecendo por baixo dos panos. Ou seja, o JavaScript não é uma linguagem baseada em classes, mas sim, continua sendo baseada em protótipos.

Extends:

É um recurso da OOP, que uma classe herda recursos de outra classe pai, mas possui recursos extras que a classe pai não possui.

Por exemplo, a classe crianças pode ter propriedades extras, como quantidade de brinquedos.

Vamos ver:

class Criancas extends Pessoas {
    constructor(nome, idade, humor, qtdBrinquedos){
        super(nome, idade, humor);
        this.qtdBrinquedos = qtdBrinquedos;
    }
    meusBrinquedos(){
        return "Eu tenho " + this.qtdBrinquedos + " brinquedos!";
    }
}

let dudu = new Criancas("Eduardo", 6, "Bem humorado", 12);
console.log(dudu);

Com isso, vimos que, utilizando a palavra-chave extends, utilizamos propriedades existentes na classe pai (Pessoas), como nome, idade e humor. Mas também, a nossa classe Crianças possui um outro atributo, chamado qtdBrinquedos, que é exclusivo da classe Crianças, ou seja, não existe na classe pai (Pessoas). O resultado no nosso console é:

Pessoas { nome: 'João', idade: 25, humor: 'bem humorado' }
Criancas {
  nome: 'Eduardo',
  idade: 6,
  humor: 'Bem humorado',
  qtdBrinquedos: 12
}

E podemos também utilizar os métodos da nossa classe pai. Por exemplo, podemos ver que nossa criança pode cantar, dançar e nos dizer quantos brinquedos possui:

console.log(dudu.cantar());
console.log(dudu.dancar());
console.log(dudu.meusBrinquedos());

//Eduardo está cantando!
//Eduardo está dançando!
//Eu tenho 12 brinquedos!

E se você mostrar o conteúdo da variável dudu no console, verá que possui uma propriedade __proto__ que faz referência ao construtor Crianças e também obtém acesso ao método meusBrinquedos(). Essa __proto__ também possui uma propriedade __proto__, que faz referência ao construtor Pessoas, obtendo acesso aos seus métodos: cantar() e dancar(). Nome, idade e humor são propriedades que existem em todos objetos que herdam as propriedades da classe Pessoas.

Usando o Object.create, o código se traduz em:

function Pessoas(nome, idade, humor){
    let novaPessoa = Object.create(pessoaConstructor);
    novaPessoa.nome = nome;
    novaPessoa.idade = idade;
    novaPessoa.humor = humor;
    return novaPessoa;
}

let pessoaConstructor = {
    cantar: function(){
        return this.nome + " está cantando!";
    },
    dancar: function(){
        return this.nome + " está dançando!";
    }
}

function Criancas(nome, idade, humor, qtdBrinquedos) {
    let novaCrianca = Pessoas(nome, idade, humor);
    Object.setPrototypeOf(novaCrianca, criancaConstructor);
    novaCrianca.qtdBrinquedos = qtdBrinquedos;
    return novaCrianca;
}

let criancaConstructor = {
    meusBrinquedos: function(){
        return "Eu tenho " + this.qtdBrinquedos + " brinquedos!";
    }
}

Object.setPrototypeOf(criancaConstructor, pessoaConstructor);
const dudu = Criancas("Eduardo", 6, "bem humorado", 12);
console.log(dudu.cantar());
console.log(dudu.dancar());
console.log(dudu.meusBrinquedos());

//Eduardo está cantando!
//Eduardo está dançando!
//Eu tenho 12 brinquedos!

O método Object.setPrototypeOf recebe dois argumentos:

  • O objeto (primeiro argumento);
  • O protótipo desejado (segundo argumento).

A função Pessoas retorna um objeto protótipo com o pessoaConstructor. A função Criancas retorna um objeto protótipo de criancaConstructor; criancaConstructor, por outro lado, é dadum um protótipo de pessoaConstructor.

Gostou deste artigo? Comente abaixo!

Referências: https://www.freecodecamp.org/news/how-javascript-implements-oop/

JavaScript OOP: Uma introdução parte 1

Por definição, sabemos que uma linguagem orientada a objetos tem como característica principal o modelo de linguagem baseada em classes. Sabemos que JavaScript não é uma linguagem baseada em classes, mas sim, baseada em protótipos. Uma linguagem baseada em prototypes tem a noção de um objeto protótipo, usado como modelo para se obter as propriedades iniciais de um novo objeto.

Por exemplo:

let carros = {
    carro1: "Gol",
    carro2: "Uno"
}

console.log(carros.carro1);
console.log(carros.hasOwnProperty("carro3"));
//gol
//false

O objeto carros possui duas propriedades: carro1 e carro2, e nenhum outro método.

E o hasOwnProperty? Vem do Objeto Protótipo.

Expandindo o objeto carros no console, vemos que existe uma propriedade __proto__. Expandindo a propriedade __proto__, vemos que o hasOwnProperty está lá!

Todos os objetos têm acesso ao Objeto Prototype. Eles não possuem as propriedades, mas têm acesso concedido às propriedades no protótipo.

A propriedade __proto__:

Ela aponta para o objeto que é usado como protótipo. Esta propriedade em cada objeto, permite acesso ao Object prototype!

Todo objeto, por padrão, possui essa propriedade, exceto quando o __proto__ é apontado para outro protótipo. Podemos também modificar esta propriedade, declarando explicitamente que ela deve se referir a outro protótipo. Os seguintes métodos são usados para conseguir isso:

Object.create():

function ObjetoPessoa(nome, idade){
    let pessoa = Object.create(objetoConstrutor);
    pessoa.nome = nome;
    pessoa.idade = idade;
    return pessoa;
}

let objetoConstrutor = {
    falar: function(){
        return "Olá";
    }
}

let joao = ObjetoPessoa("João", 54);
console.log(joao);

Observe o método falar na propriedade __proto__. O Object.create usa o argumento passado para ele para se tornar o protótipo.

Utilizando o new:

A propriedade __proto__ é direcionada ao protótipo de ObjetoPessoa, mas este é um objeto (par de chave e valor), portanto, ele também possui uma propriedade __proto__ que refere-se ao protótipo global Object. Esta técnica é chamada de PROTOTYPE CHAINING.

O New faz a mesma coisa que o Object.create(), apenas facilitando-o, pois faz algumas coisas automaticamente para você:

function ObjetoPessoa(nome, idade){
    this.nome = nome;
    this.idade = idade;
}

ObjetoPessoa.prototype.falar = function(){
    return "Olá!"
}

let joao = new ObjetoPessoa("João", 45);

CLASS:

O ECMAScript 2015 introduziu a palavra-chave class, fazendo com que o javaScript se pareça com uma linguagem OOP. Mas é apenas um enfeite sobre a técnica de prototipagem existente, pois ele continua utilizando protótipos em segundo plano, apenas fazendo com que o corpo externo do código se pareça com OOP. Vejamos:

class Pessoas {
    constructor(nome, idade, humor){
        this.nome = nome;
        this.idade = idade;
        this.humor = humor;
    }

    cantar(){
        return this.nome + " está cantando!";
    }

    dancar(){
        return this.nome + " está dançando!";
    }
}

let joao = new Pessoas("João", 25, "bem humorado");
console.log(joao);

E esse seria o resultado no console:

Veja que a propriedade __proto__ faz referências ao protótipo Pessoas (que faz referência ao protótipo Object). Podemos ver que o construtor define os principais recursos, enquanto cantar() e dancar() são os protótipos.

O código acima, sem utilizar a palavra-chave class, seria:

function Pessoas(nome, idade, humor){
    this.nome = nome;
    this.idade = idade;
    this.humor = humor;
}

Pessoas.prototype.cantar() = function{
    return this.nome + " está cantando!";
}

Pessoas.prototype.dancar() = function{
    return this.nome + " está dançando!";
}

let joao = new Pessoas("João", 25, "bem humorado");

Gostou deste artigo? Comente abaixo!

Referências: https://www.freecodecamp.org/news/how-javascript-implements-oop/