Estrutura de Dados e Classes JavaScript: Deque

O Deque (Fila duplamente Encadeada) funciona basicamente com uma fila, com a diferença de que você pode adicionar e remover dados de qualquer extremidade da fila. Isso torna o nosso código um pouco mais difícil, mas chegaremos lá. Continuaremos usando class para realizar nossa estrutura de dados. Precisamos, inicialmente, criar a nossa classe Deque. Ela precisará de um início (head), um final (tail) e um contador:

class Deque {
    constructor(head = null, tail = null, count = 0){
        this.head = head;
        this.tail = tail;
        this.count = count;
    }
}

Também precisaremos criar os métodos GetHead, GetTail e GetCount:

GetHead(){
    if (this.head){
        return this.head.data;
    }
    return null;
}

GetTail(){
    if (this.tail){
        return this.tail.data;
    }
    return null;
}

GetCount(){
    return this.count;
}

A nossa fila duplamente encadeada terá os seguintes métodos:

  • MostrarInicioFim();
  • MostrarFimInicio();
  • AdicionarInicio(data);
  • AdicionarFim(data);
  • RemoverInicio();
  • RemoverFim().

Para criarmos nossos métodos, criaremos nosso código de forma diferente aos anteriores. Utilizaremos uma Classe de auxílio, para evitar reescrever código:

class Node{
    constructor(data, next = null){
        this.data = data;
        this.next = next;
    }
}

MostrarInicioFim():

Esse método retorna um array com os dados, percorrendo do início ao fim:

MostrarInicioFim(){
    if (this.head != null){
        let arr = [];
        let current = this.head;
        
        while (current){
            arr.push(current.data);
            current = current.next;
        }
        return arr;
    } else {
        return null;
    }
}

MostrarFimInicio():

Semelhante ao nosso método anterior, ele retorna um array com os dados da fila, porém, percorrendo do fim ao início. Para isso, utilizaremos o reverse():

MostrarFimInicio(){
    if (this.head != null){
        let arr = this.MostrarInicioFim();
        return arr.reverse();
    } else {
        return null;
    }
}

AdicionarInicio(data):

Adiciona o dado no início de nossa fila e, para isso, usaremos nossa classe Node criada anteriormente para auxílio. Precisamos também incrementar o nosso contador:

AdicionarInicio(data){
    let no = new Node(data);
    no.next = this.head;
    this.head = no;

    if (!this.tail) {
        this.tail = this.head;
    }
    this.count++;
}

AdicionarFim(data):

Esse método adicionar o dado no fim da fila. Semelhante ao nosso método anterior, usaremos a nossa classe Node de apoio. Precisamos também, nesse método, incrementar nosso contador.

AdicionarFim(data){
    let no = new Node(data);
    if (!this.head){
        this.head = no;
    } else {
        this.tail.next = no;
    }
    this.tail = no;
    this.count++;
}

RemoverInicio():

Remove o dado que está no início (head) de nossa fila duplamente encadeada.

RemoverInicio(){
    if (this.head){
        if (this.count === 1){
            this.head = null;
            this.tail = null;
        } else {
            this.head = this.head.next;
        }
        this.count--;
    }
}

RemoverFim():

Remove o dado que está na posição final da nossa fila:

RemoverFim(){
    if (this.head){
        if (this.count === 1){
            this.head = null;
            this.tail === null;
        } else {
            let current = this.head;

            while(current.next.next){
                current = current.next;
            }

            this.tail = current;
            this.tail.next = null;
        }
        this.count--;
    }
}

Gostou deste artigo? Comente abaixo!

Estrutura de Dados e Classes JavaScript: Lista Encadeada

Uma lista encadeada é uma sequência de objetos, onde cada elemento da sequência é armazenado em uma célula (nó) desta lista. O primeiro fica na primeira célula, o segundo na segunda célula e assim, sucessivamente. Cada célula possui seu próprio conteúdo. Você vai perceber as semelhanças entre Lista Encadeada, Fila e Pilha, visto que estas duas últimas foram criadas usando a ideia básica da Lista Encadeada. Seguindo nossa sequência, criaremos esta estrutura usando CLASS. Vamos definir nossa Lista Encadeada. Ela precisará de um Head para iniciar a lista e também um contador:

class ListaEncadeada{
    constructor(head = null, count = 0){
        this.head = head;
        this.count = count;
    }

    GetContador(){
        return this.count;
    }
}

Nossa lista terá os seguintes métodos:

  • MostrarTudo();
  • MostrarEm(index);
  • AdicionarInicio(data);
  • AdicionarNaPosicao(data, index);
  • RemoverInicio();
  • RemoverNaPosicao(index).

Vamos lá!

MostrarTudo():

Como o próprio nome já diz, esse método mostra todos os elementos que estão na lista. Ele retornará um array contendo os dados e, se a lista estiver vazia, retorna null:

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

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

MostrarEm(index):

Nosso método MostrarEm nos retorna o elemento que está na posição desejada (index). Caso a posição buscada seja inexistente, o método retornará null:

MostrarEm(index){
    if (index > -1 && index < this.count){
        let current = this.head;

        let i = 0;
        while (i++ < index){
                current = current.next;
        }
        return current.data;
    } else {
        return null;
    }
}

AdicionarInicio(data):

O nosso método adicionará o dado no início da lista. Caso esteja se perguntando, o início da lista é onde o índice é 0 e é referenciado pelo head. Precisamos incrementar o contador ao realizar a inserção:

AdicionarInicio(data){
    let no = {
        data: data,
        next: this.head
    };

    this.head = no;
    this.count++;
}

AdicionarNaPosicao(data, index):

Adiciona um item na posição determinada. Precisamos verificar se não está fora dos limites da nossa lista encadeada. Caso não exista essa posição, mostraremos uma mensagem no console:

AdicionarNaPosicao(data, index){
    if (index === 0) {
        this.AdicionarInicio(data);
    } else if (index > -1 && index < this.count){
        let no = {
            data: data,
            next: null
        };

        let previous;
        let current = this.head;
        let i = 0;

        while (i++ < index){
            previous = current;
            current = current.next;
        }

        previous.next = no;
        no.next = current;

        this.count++;
    } else {
        console.log("Não existe esta posição na lista.")
    }
}

RemoverInicio():

O método RemoverInicio() remove o primeiro item da lista. Caso a lista esteja vazia, o método retorna null.

RemoverInicio(){
    if (this.head === null){
        return null;
    } else {
        let out = this.head;
        this.head = this.head.next;

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

        return out.data;
    }
}

RemoverNaPosicao(index):

Remove o item que está na posição especificada. Precisamos novamente verificar se o índice está dentro dos limites da nossa lista:

RemoverNaPosicao(index){
    if (index === 0) {
        return this.RemoverInicio(this);
    } 
        
    else if ( index > -1 && index < this.count){
            
        let current = this.head;
        let previous;
        let i = 0;

        while (i++ < index){
            previous = current;
            current = current.next;
        }

        previous.next = current.next;
        this.count--;

        return current.data;
    } else {
        return null;
    }    
}

Gostou deste artigo? Comente abaixo!

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!

 

Estrutura de Dados e Classes JavaScript: Fila

Filas são estruturas de dados onde o primeiro a entrar é o primeiro a sair. É chamado de FIFO (First In First Out). As operações em Fila são análogas ao que ocorre em filas reais, como a fila de um banco, por exemplo, com a exceção de que os elementos na fila não se movem, conforme o primeiro elemento é retirado. Na estrutura de dados Fila, o algoritmo indica quem está na primeira posição.

Utilizaremos as classes que foram implementadas no ECMAScript 2015 para criação de nossa fila. A nossa estrutura terá os seguintes métodos:

  • Enqueue (inserção na fila);
  • Dequeue (remoção da fila);
  • MostrarTudo;
  • VisualizarEm.

Para começar, precisamos criar uma classe chamada Fila, onde teremos o início da fila (head), fim da fila (tail) e um contador (count). Como a fila está vazia, o nosso head e nosso tail recebem o valor null, e nosso contador começa em 0. Precisamos também de um método Get para nosso contador:

class Fila {
    constructor(head = null, tail = null, count = 0){
        this.head = head;
        this.tail = tail;
        this.count = count;
    }

    GetContador(){
        return this.count;
    }
}

Enqueue:

Para adicionarmos um item no início da nossa fila, precisamos criar um nó, que conterá o dado e uma referência à próxima posição da fila, que deverá ser null. Quando inserirmos nosso primeiro elemento, o início e o final da fila serão o mesmo elemento, e precisaremos incrementar o contador:

Enqueue(data){
    let no = {
        data: data,
        next: this.head
    };

    if (this.head === null){
        this.tail = no;
    }

    this.head = no;
    this.count++;

}

Dequeue:

Removeremos o primeiro elemento da fila e retornaremos o último item inserido e armazenado no final da fila. Fazendo isso, teremos que, enquanto houver um próximo elemento, avançaremos na fila. A ideia é ter um atual no final da fila e um anterior e, então, teremos que decrementar o contador:

Dequeue(){
    if (this.count === 0){
        return;
    } else {
        let current = this.head;
        let previous = null;

        while (current.next){
            previous = current;
            current = current.next;
        }

        if (this.count > 1){
            previous.next = null;
            this.tail = previous;
        } else {
            this.head = null;
            this.tail = null;
        }
        this.count--;
    }
}

MostrarTudo:

O próprio nome já diz: mostraremos todos os elementos da fila. Utilizaremos um vetor para isso e, com um laço de repetição, iremos inserir cada valor em sua respectiva posição neste vetor. Também precisamos verificar se há elementos na fila:

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

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

VisualizarEm:

No método VisualizarEm, iremos procurar o elemento na nossa fila, buscando-o e exibindo-o. Utilizaremos um laço de repetição para percorrer nossa fila e encontrar o elemento que buscamos:

VisualizarEm(index){
    if (index > -1 && index < this.count){
        let current = this.head;

        for (let i = 0; i < index; i++) {
            current = current.next;
        }

        return current.data;
    } else {
        return null;
    }
}

Agora, vamos testar:

let fila = new Fila();
fila.Enqueue(1);
fila.Enqueue(2);
fila.Enqueue(3);
fila.Enqueue(4);
fila.Enqueue(5);
fila.Enqueue(6);
fila.Dequeue();
fila.Dequeue();
console.log(fila.VisualizarEm(1));
console.log(fila.MostrarTudo());
console.log(fila);

E a nossa 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/

Entendendo o Prototype

Uma das formas de criar objetos em JavaScript é usando a função construtora (constructor). Mas há um problema em criar objetos com o Constructor, pois ao executar uma função construtora, criando-se objetos, a engine JavaScript cria duas cópias da função construtora, ou seja, todo objeto criado utilizando a função Constructor terá a sua própria cópia de propriedades e métodos. Ou seja, torna-se redundante, pois não há sentido em ter duas instâncias de uma função fazendo a mesma coisa. Por exemplo:

function Humanos(nome, sobrenome){
    this.nome = nome;
    this.sobrenome = sobrenome;
    this.nomeCompleto = function(){
        return this.nome + " " + this.sobrenome;
    }
}


let pessoa1 = new Humanos("João", "Silva");
let pessoa2 = new Humanos("José", "Silveira");
console.log(pessoa1);
console.log(pessoa2);

Utilizando o console.log, verificamos a criação de duas funções nomeCompleto(), e isso resulta em desperdício de memória.

E como resolver isso?

Ao criar uma função, o mecanismo JavaScript adiciona um protótipo (prototype) à função. Essa propriedade é um objeto denominado protótipo de objeto e possui um constructor por padrão. Esse constructor aponta de volta para a função na qual o prototype é uma propriedade. Podemos acessar a propriedade prototype da função usando nomeFuncao.prototype.

No nosso exemplo, a função Humanos possui uma propriedade prototype que aponta para o objeto prototype. O objeto prototype tem uma propriedade constructor e aponta de volta para a função Humanos. Confuso? Vamos verificar isso na nossa imagem:

Para acessar nossa propriedade prototype da função Humanos:

console.log(Humanos.prototype);

E isso nos mostrará que a função possui um objeto prototype com dua propriedades:

  • constructor: aponta para a própria função Humanos;
  • __proto__: propriedade utilizada no conceito de herança.

O Objeto pessoa1 criado usando a função construtora Humanos possui um __proto__ que aponta para o protótipo de objeto da função constructor.

Podemos verificar que o protótipo pessoa1.__proto__ e a propriedade Humanos.prototype são iguais:

Humanos.prototype === pessoa1.__proto__;
//true

Criaremos agora um novo objeto, chamado pessoa 2:

let pessoa2 = new Humanos("José", "Silveira");

Verificaremos que o protótipo pessoa2.__proto__ também é igual a propriedade Humanos.prototype:

Humanos.prototype === pessoa2.__proto__;
//true

E verificamos que a propriedade __proto__ dos dois objetos pessoas são estritamente iguais! Veja só:

pessoa1 === pessoa2;
//false

pessoa1.__proto__ === pessoa2.__proto__;
//true

E isso prova que as propriedades __proto__ de pessoa1 e pessoa2 apontam para o Humanos.prototype que criamos anteriormente!

Como o prototype é um objeto, podemos anexar propriedades e métodos ao objeto protótipo. Assim, permite que os objetos criados usando a função construtora compartilhem essas propriedades e métodos:

Humanos.prototype.nome = "Maria";
console.log(Humanos.prototype.nome);

Humanos.prototype["idade"] = 30;
console.log(Humanos.prototype["idade"]);

console.log(Humanos.prototype);

Você pode ver que as propriedades nome e idade foram adicionadas ao Humanos.prototype!

Quando tentamos acessar uma propriedade de um objeto, o JavaScript primeiro tenta encontrar a propriedade no objeto e, se a propriedade estiver presente no objeto, ele gera o seu valor. Se não estiver, ele tentará encontrar a propriedade no objeto prototype. Se for encontrada, o valor será retornado.

Exemplo:

Primeiro, criaremos uma função construtora vazia:

function Pessoa(){

}

Depois, adicionaremos as propriedades nome, idade e digaOla ao prototype da função Pessoa:

Pessoa.prototype.nome = "Maria";
Pessoa.prototype.idade = 30;
Pessoa.prototype.digaOla = function(){
    console.log(this.nome + " diz Olá para você!");
};

Criaremos um objeto usando a função construtora Pessoa e acessaremos a propriedade nome usando o objeto criado:

let pessoa1 = new Pessoa();
console.log(pessoa1.nome);

E a saída será?

Maria

Gostou deste artigo? Comente abaixo!

Referência: https://medium.com/better-programming/prototypes-in-javascript-5bba2990e04b

jQuery: Utilizando o plugin Mask

Neste artigo, aprenderemos a importar e utilizar o Mask, criado por Igor Escobar e usa como base o jQuery.

O Mask permite que você crie máscaras personalizadas para os seus campos de input. Isso auxilia no formato dos seus dados. Por exemplo, se você quiser criar uma máscara para o seu input que captura a data, você consegue personalizar criando algo no formato (’00/00/0000′).

Download do plugin:

Você pode realizar o download do Mask no site:

http://igorescobar.github.io/jQuery-Mask-Plugin/

Após o download, você deve inserir a biblioteca no seu HTML utilizando a tag <script>.

Você também pode utilizar diretamente o CDN para importar sua biblioteca ao seu projeto, utilizando o seguinte comando:

<script src="hhttps://cdnjs.com/libraries/jquery.mask"></script>

É importante salientar que você precisa importar o jQuery no seu <head> do seu projeto. Você pode importar o jQuery do seu projeto utilizando o CDN:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

Como é a sintaxe?

É uma sintaxe simples, onde primeiramente é passado o seletor, seguido do .mask e o parâmetro que será utilizado. Por exemplo, no seu arquivo HTML você tem um campo de input para receber uma data:

<label>DATA</label>
<input type="text" class="date">

No seu arquivo de scripts, tudo o que você precisará para fazer a máscara funcionar é:

$(document).ready(function(){
    $('.date').mask('00/00/0000')
});

E com isso, já obtemos todas as vantagens do plugin. Essa seria a sua saída:

Mas o plugin não se aplica somente a datas. Com a utilização do Mask, podemos criar máscaras de input para CEP, CPF, Telefones e tantas outras possibilidades. Tudo que precisamos fazer é criar uma classe para cada tipo de input que utilizaremos na nossa página e utilizar essa classe no nosso script jQuery, como fizemos no exemplo anterior. Veja:

HTML:

<label>DATA</label>
<input type="text" class="date">
<label>HORAS</label>
<input type="text" class="time">
<label>CEP</label>
<input type="text" class="cep">
<label>TELEFONE</label>
<input type="text" class="phone">
<label>CPF</label>
<input type="text" class="cpf">
<label>DINHEIRO</label>
<input type="text" class="money">

E o nosso código jQuery:

$(document).ready(function(){
    $('.date').mask('00/00/0000');
    $('.time').mask('00:00:00');
    $('.cep').mask('00000-000');
    $('.phone').mask('(00) 00000-0000');
    $('.cpf').mask('000.000.000-00');
    $('.money').mask('000.000.000.000,00');
});

E teremos nossa saída:

Gostou deste artigo? Comente abaixo!

Referências: http://www.igorescobar.com/blog/2012/03/13/mascaras-com-jquery-mask-plugin/

Criando Ping Pong com JavaScript parte 3

Na nossa sequência de artigos sobre como criar um jogo Ping Pong utilizando JavaScript puro, já conseguimos renderizar nossa tela de fundo de jogo e adicionar os objetos referentes ao Jogador, ao Computador e à bolinha que serão utilizados para dar vida ao jogo. Caso você não tenha visto as partes 1 e 2 referentes a continuação deste artigo, poderá acessá-las em:

Na última parte desta nossa sequência de artigos, criaremos as animações e adicionaremos os controles, tanto do jogador quanto à IA referente ao computador.

Animação:

Começaremos adicionando o movimento. A animação da bola precisa se direcionar à raquete do jogador. Fazendo isso, alteraremos a função atualizar e adicionar essa atualização na nossa bola, com um incremento:

function atualizar(){
    bolinha.atualizar();
};

Bolinha.prototype.atualizar = function(){
    this.x += this.velocidade_x;
    this.y += this.velocidade_y;
}

As nossas pás estão paradas e precisamos verificar a existência de colisão da bola com as raquetes do jogador e computador. Também precisamos verificar se a bola atingirá as extremidades esquerda e direita do nosso campo de jogo. Também precisamos colocar as raquetes de ambos os jogadores no nosso método de atualização:

let top_x = this.x - 5;
    let top_y = this.y - 5;
    let bottom_x = this.x + 5;
    let bottom_y = this.y + 5;

    // Se bate na extremidade esquerda
    if (this.x - 5 < 0){
        this.x = 5;
        this.velocidade_x = -this.velocidade_x;
    } else if (this.x + 5 > 400){ //Se bate na extremidade direita
        this.x=395;
        this.velocidade_x = -this.velocidade_x;
    }

    //Se tocar no fim do jogo ou no início, ocorre um ponto
    if (this.y < 0 || this.y > 600){
        this.velocidade_x = 0;
        this.velocidade_y = 3;
        // E centralizamos a bolinha novamente
        this.x = 200;
        this.y = 300;
    }

    if (top_y > 300){
        if (top_y < (raquete1.y + raquete1.height) && bottom_y > raquete1.y && top_x < (raquete1.x + raquete1.width) && bottom_x > raquete1.x){
            // Acerta a raquete do jogador
            this.velocidade_y = -3;
            this.velocidade_x += (raquete1.velocidade_x /2);
            this.y += this.velocidade_y;
        }
    }else{
        if(top_y < (raquete2.y + raquete2.height) && bottom_y > raquete2.y && top_x < (raquete2.x + raquete2.width) && bottom_x > raquete2.x){
            // Acerta a raquete do computador
            this.velocidade_y = 3;
            this.velocidade_x += (raquete2.velocidade_x / 2);
            this.y += this.velocidade_y;
        }
    }

Controles:

Agora, criaremos o controle para que o jogador possa mover sua raquete, utilizando um keyDown e um addEventListener para adicionar um evento de pressionar tecla:

let keysDown = {};

window.addEventListener("keydown", function(event){
    keysDown[event.keyCode] = true;
});

window.addEventListener("keyup", function(event){
    delete keysDown[event.keyCode];
})

Agora, podemos atualizar a posição da raquete conforme a tecla for pressionada. Precisamos agora criar um código para mover a raquete ao ser pressionada a tecla:

Jogador.prototype.atualizar = function(){
    for (let key in keysDown) {
        //Captura e pega o número da tecla pressionada
        let valor = Number(key);
        // Tecla seta para esquerda (37)
        if (valor == 37) {
            this.raquete.mover(-4, 0);
        }
        // Tecla seta para direita (39)
          else if (valor == 39) {
            this.raquete.mover(4, 0);
        } else{
            this.raquete.mover(0, 0);
        }
    }
};

Raquete.prototype.mover = function(x, y){
    this.x += x;
    this.y += y;
    this.velocidade_x = x;
    this.velocidade_y = y;
    // Mover tudo para a esquerda
    if(this.x < 0){
        this.x = 0;
        this.velocidade_x = 0;
    }
    // Mover tudo para a direita
      else if (this.x + this.width > 400){
        this.x = 400 - this.width;
        this.velocidade_x = 0;
    }
}

E precisamos atualizar a nossa função atualizar:

function atualizar(){
    jogador.atualizar();
    bolinha.atualizar(jogador.raquete, computador.raquete);
};

Inteligência do Computador:

Já podemos controlar nossa raquete e a bolinha vai ricochetear de acordo com o movimento da raquete, mas ainda não temos movimento no nosso adversário, o computador. Vamos fazer a raquete do computador sempre se posicionar conforme o centro da bolinha. O computador terá velocidade máxima para ele pontuar ocasionalmente:

Computador.prototype.atualizar = function (bolinha){
    let posicao_x = bolinha.x;
    let diferenca = -((this.raquete.x + (this.raquete.width / 2)) - posicao_x);
    // Máxima velocidade para a esquerda
    if (diferenca < 0 && diferenca < -4) {
        diferenca = -5;
    } 
    // Máxima velocidade para a direita  
      else if (diferenca > 0 && diferenca > 4){
        diferenca = 5;
    }
    this.raquete.mover(diferenca, 0);
    if (this.raquete.x < 0){
        this.raquete.x = 0;
    } else if (this.raquete.x + this.raquete.width > 400){
        this.raquete.x = 400 - this.raquete.width;
    }
}

E atualizamos a nossa função atualizar:

function atualizar(){
    jogador.atualizar();
    computador.atualizar(bolinha)
    bolinha.atualizar(jogador.raquete, computador.raquete);
};

Com isso, temos uma réplica do jogo de Ping Pong funcional! Teste no seu navegador!

Gostou desta sequência de artigos? Comente abaixo!

Referências: https://thoughtbot.com/blog/pong-clone-in-javascript

 

Criando Ping Pong com JavaScript parte 2

Na parte 1 do nosso jogo, criamos a tela renderizada onde nosso jogo irá acontecer. Também criamos uma função que renderiza essa tela assim que a nossa página HTML é carregada. Você pode conferir os nossos códigos em:

Na nossa continuação, criaremos as nossas raquetes e a bola a ser utilizada no nosso jogo de Ping Pong. Como sabemos, temos dois jogadores, o Player 1 e o Player 2 (que no nosso jogo, será uma pequena inteligência artificial).

Nossos objetos deverão ser renderizados em tela. Precisamos fornecer uma posição x, y, uma altura e uma largura. Também precisamos criar velocidades para nossa bolinha. Essas velocidades atuarão no eixo X e no eixo Y:

function Raquete(x, y, width, height){
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.velocidade_x = 0;
    this.velocidade_y = 0;
}

Com nossa função Raquete criada, precisamos renderizar as raquetes agora. Criaremos um objeto protótipo de nossa raquete, utilizando o Prototype. A cor da nossa raquete será preta.

function Raquete(x, y, width, height){
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.velocidade_x = 0;
    this.velocidade_y = 0;
}

Raquete.prototype.renderizar = function(){
    contexto.fillStyle = "black";
    contexto.fillRect(this.x, this.y, this.width, this.height);
};

Já que cada raquete será controlada de forma independente, vamos criar objetos para representá-las. As coordenadas X e Y são escolhidas para colocar o Jogador na parte inferior, e o Computador na parte superior:

function Jogador(){
    this.raquete = new Raquete(175, 580, 50, 10);
}

function Computador(){
    this.raquete = new Raquete(175, 10, 50, 10);
}

E precisamos criar a função para renderizar o jogador e o computador:

Jogador.prototype.renderizar = function(){
    this.raquete.renderizar();
}

Computador.prototype.renderizar = function(){
    this.raquete.renderizar();
}

E precisamos também criar a bola. Ela será um círculo, com coordenadas x e y que representarão o centro do círculo. Também forneceremos um raio de 5, ou seja, a nossa bolinha terá 10 de diâmetro:

function Bolinha(x, y){
    this.x = x;
    this.y = y;
    this.velocidade_x = 0;
    this.velocidade_y = 3;
    this.radius = 5;
}

Bolinha.prototype.renderizar = function() {
    contexto.beginPath();
    contexto.arc(this.x, this.y, this.radius, 2 * Math.PI, false);
    contexto.fillStyle="white";
    contexto.fill();
};

E agora, vamos construir nossos objetos Jogador, Computador e Bolinha e atualizar a nossa função renderizar():

let jogador = new Jogador();

let computador = new Computador();

//A bolinha será renderizada no centro da tela
let bolinha = new Bolinha(200,300);
let renderizar = function(){
    contexto.fillStyle = "#836FFF";
    contexto.fillRect(0, 0, width, height);
    //Atualizar a partir daqui
    jogador.renderizar();
    computador.renderizar();
    bolinha.renderizar();
}

E a nossa saída será:

Gostou deste artigo? Comente abaixo e acompanhe a sequência!

Referência: https://thoughtbot.com/blog/pong-clone-in-javascript