Herança e protótipos em Java Script
Em JavaScript, a herança e o sistema de protótipos são conceitos centrais para o comportamento orientado a objetos da linguagem. Diferente de outras linguagens como Java ou C#, que utilizam herança baseada em classes, o JavaScript implementa a herança baseada em protótipos, o que o torna único e mais flexível. Neste post, vamos entender o que são herança e protótipos em Java Script, e veremos alguns exemplos práticos.
O que é Herança?
Herança é um princípio da programação orientada a objetos que permite que um objeto derive propriedades e métodos de outro objeto. Isso ajuda a reutilizar código, evitando a duplicação de funcionalidades comuns.
Em linguagens baseadas em classes, a herança é feita por classes que estendem outras classes. Já no JavaScript, a herança é feita através de protótipos, que são uma forma de objetos herdarem características de outros objetos.
O que são Protótipos?
Todo objeto em JavaScript tem uma referência interna chamada [[Prototype]]
, que aponta para outro objeto, conhecido como protótipo. Este protótipo pode ter outro protótipo, formando uma cadeia de protótipos conhecida como cadeia de protótipos (prototype chain).
A grande vantagem dos protótipos é que, se uma propriedade ou método não for encontrado diretamente no objeto, o JavaScript procurará no protótipo desse objeto, e assim por diante, até encontrar a propriedade ou chegar ao final da cadeia.
Como funciona a Herança em JavaScript?
Em JavaScript, você pode criar novos objetos herdando propriedades e métodos de outros objetos. A herança é implementada através da propriedade especial prototype
em funções e classes.
Exemplo básico de herança via protótipo:
// Definindo um objeto "Pessoa"
function Pessoa(nome, idade) {
this.nome = nome;
this.idade = idade;
}
// Adicionando um método ao protótipo de "Pessoa"
Pessoa.prototype.falar = function() {
console.log(`Meu nome é ${this.nome} e eu tenho ${this.idade} anos.`);
};
// Criando uma nova instância de "Pessoa"
const pessoa1 = new Pessoa("João", 30);
// Usando o método herdado via protótipo
pessoa1.falar(); // Saída: Meu nome é João e eu tenho 30 anos.
Nesse exemplo, o método falar
foi adicionado ao protótipo de Pessoa
. Isso significa que todas as instâncias de Pessoa
(como pessoa1
) têm acesso a esse método.
A cadeia de Protótipos
A cadeia de protótipos é o mecanismo pelo qual JavaScript resolve propriedades e métodos não encontrados diretamente em um objeto. Se você tentar acessar uma propriedade em um objeto e ela não existir, o JavaScript vai procurar essa propriedade no protótipo do objeto, depois no protótipo do protótipo, e assim por diante.
Exemplo da cadeia de protótipos:
// Definindo um objeto "Animal"
function Animal(tipo) {
this.tipo = tipo;
}
// Adicionando um método ao protótipo de "Animal"
Animal.prototype.comer = function() {
console.log(`${this.tipo} está comendo.`);
};
// Definindo um objeto "Cachorro" que herda de "Animal"
function Cachorro(nome) {
this.nome = nome;
Animal.call(this, 'Cachorro'); // Chama o construtor de Animal
}
// Herdando o protótipo de "Animal"
Cachorro.prototype = Object.create(Animal.prototype);
// Adicionando um método ao protótipo de "Cachorro"
Cachorro.prototype.latir = function() {
console.log(`${this.nome} está latindo.`);
};
// Criando uma instância de "Cachorro"
const dog = new Cachorro("Rex");
// Chamando métodos herdados e próprios
dog.comer(); // Saída: Cachorro está comendo.
dog.latir(); // Saída: Rex está latindo.
Nesse exemplo, Cachorro
herda o método comer
de Animal
, utilizando a cadeia de protótipos. Isso acontece porque o protótipo de Cachorro
é uma instância de Animal
.
Protótipos vs Classes (ES6)
Com o lançamento do ES6, o JavaScript introduziu a sintaxe de classes, que fornece uma maneira mais amigável e concisa de trabalhar com herança e objetos. No entanto, é importante notar que, por baixo dos panos, as classes em JavaScript ainda utilizam protótipos.
Exemplo de herança usando classes ES6:
class Animal {
constructor(tipo) {
this.tipo = tipo;
}
comer() {
console.log(`${this.tipo} está comendo.`);
}
}
class Cachorro extends Animal {
constructor(nome) {
super('Cachorro'); // Chama o construtor da classe pai (Animal)
this.nome = nome;
}
latir() {
console.log(`${this.nome} está latindo.`);
}
}
const dog = new Cachorro("Rex");
dog.comer(); // Saída: Cachorro está comendo.
dog.latir(); // Saída: Rex está latindo.
A sintaxe de classes oferece uma maneira mais intuitiva de escrever código orientado a objetos em JavaScript, mas o conceito de protótipos permanece inalterado.
Propriedade prototype
e métodos como Object.create
Além da herança via construtores e classes, JavaScript também oferece a função Object.create
para criar novos objetos com protótipos específicos.
Exemplo usando Object.create
:
const animal = {
tipo: 'Desconhecido',
comer() {
console.log(`${this.tipo} está comendo.`);
}
};
const cachorro = Object.create(animal);
cachorro.tipo = 'Cachorro';
cachorro.latir = function() {
console.log('O cachorro está latindo.');
};
cachorro.comer(); // Saída: Cachorro está comendo.
cachorro.latir(); // Saída: O cachorro está latindo.
Nesse exemplo, cachorro
herda do protótipo animal
, e você pode adicionar novos métodos e propriedades diretamente a cachorro
.
Vantagens da Herança por Protótipos
- Flexibilidade: Qualquer objeto pode ser usado como protótipo de outro, o que permite maior flexibilidade e reutilização de código;
- Performance: Objetos criados a partir de protótipos compartilham métodos, economizando memória;
- Customização: É possível adicionar ou modificar propriedades e métodos em protótipos a qualquer momento.
Outros exemplos
Protótipo é o mecanismo pelo qual os objetos em JavaScript herdam recursos uns dos outros através de cadeias de protótipos:
const chevrolet = {
modelo: 'Celta',
velMax: 200
}
cont volkswagen = {
modelo: 'Fox 2007',
velMax: 180
}
/* com __proto__ (underline, underline proto underline, underline) acessamos o
protótipo, super objeto ou o objeto pai do objeto volkswagen */
console.log(volkswagen.__proto__)
Significa que, se não for encontrado um atributo específico no objeto volkswagen, será procurado dentro do protótipo ou no objeto pai.
Se não achar, busca no protótipo do protótipo e assim por diante (cadeia de protótipos) até encontrar. Se não encontrar retorna undefined:
const avo = { attr1: 'X' }
const pai = { __proto__: avo, attr2: 'Y' } //cria outro atributo no protótipo 'avo'
const filho = { __proto__: pai, attr3: 'Z' } //cria outro atributo no protótipo 'pai'
console.log(filho.attr1) //imprime 'X', pois o atributo foi encontrado no protótipo herdado 'avo'
Se tentarmos acessar um atributo que não esteja em nenhum protótipo, mas um atributo criado utilizando a estrutura Object.prototype.atributo = valor, será possível acessá-lo, ja que o protótipo ‘avo’ aponta naturalmente para Object.prototype:
Object.prototype.attr4 = 'H'
const avo = { attr1: 'X' }
console.log(filho.attr4) //imprime 'H'
Para estabelecer uma relação de protótipo entre dois objetos utilizamos a função setPrototypeOf():
const carro = {
velAtual: 0,
velMax: 250,
acelerar(delta) {
if(this.velAtual + delta <= this.velMax) {
this.velAtual += delta
} else {
this.velAtual = this.velMax
}
},
status() {
return `${this.velAtual}Km/h de ${this.velMax}Km/h`
}
}
const celta = {
modelo: 2010,
velMax: 200,
status() {
//super chama o método status() do protótipo (carro), não deste objeto this
return `${this.modelo}: ${super.status()}`
}
}
/* foi estabelecida uma relação entre o objeto 'celta' e o protótipo 'carro',
ou seja, 'celta' tem 'carro' como seu protótipo ('celta' herda de 'carro')*/
Object.setPrototypeOf(celta, carro)
console.log(celta) // imprime { modelo: 2010, velMax: 200 }
celta.acelerar(110) //chama método 'acelerar' do protótipo 'carro' passando parâmetro
console.log(celta.status()) //imprime 2010: 110Km/h de 200Km/h
O método Object.create() cria um novo objeto, utilizando um outro objeto existente como protótipo para o novo objeto a ser criado:
const pai = { nome: 'José', idade: 32 }
const filha = Object.create(pai) //cria novo objeto tendo como protótipo o objeto 'pai'
filha.nome = 'Ana' //altera-se o nome
//cria objeto e o faz herdar de 'pai'
const filha2 = Object.create(pai, {
//atributo que não pode ser editado (writable: false) mas pode ser listado (enumerable: true)
nome: { value: 'Marcelo', writable: false, enumerable: true }
})
//imprime o conteúdo do objeto 'filha2' e do objeto herdado 'pai':
for(let key in filha2) {
console.log(key)
}
//imprime o conteúdo do objeto 'filha2' e do objeto herdado 'pai', mas mostra se o atributo foi herdado ou não:
for(let key in filha2) {
filha2.hasOwnProperty(key) ? //verifica se o atributo é do objeto ou foi herdado do protótipo ou da cadeia de protótipos
console.log(key) : console.log(`Por herança: ${key}`)
}
/* Imprimirá:
nome
Por herança: idade
*/
Toda função em JavaScript tem um atributo chamado “prototype”. Exemplos:
//reverter uma string
String.prototype.reverse = function () {
//quebra a string, armazena os caracteres em um array revertendo a ordem e junta novamente
return this.split('').reverse().join('')
}
console.log('CriandoBits'.reverse())//imprime stiBodnairC
//retorna a primeira posição de um array:
Array.prototype.first = function() {
return this[0]
}
console.log([3, 5, 5, 9].first()) //imprime 3
A herança por protótipos é um conceito poderoso e central para o JavaScript. Embora possa parecer estranho para quem vem de linguagens baseadas em classes, ele oferece uma maneira mais flexível e eficiente de trabalhar com objetos.
Com a introdução das classes em ES6, o JavaScript tornou-se mais acessível para programadores acostumados com herança baseada em classes, mas a essência da herança por protótipos permanece como o coração da linguagem.
Aprenda através de projetos reais e aulas práticas. São 20 cursos completos + cursos bônus. Grupos privados exclusivos, atualizações constantes e lives semanais.
Python, PHP, Java Script, CSS, Node, Angular JS, MySQL, Photoshop, Flutter, AWS, Apache e muito mais!
Link do curso: https://go.hotmart.com/X68198266R
Dúvidas ou sugestões sobre herança e protótipos em Java Script? Deixem nos comentários! Para mais dicas, acesse o nosso canal no YouTube:
https://youtube.com/criandobits