Page cover

7.8.4 - Classes e Objetos

🧬 Herança em JavaScript — O Poder de Reaproveitar Código!

🧬 Herança em JavaScript — O Poder de Reaproveitar Código!

A herança permite que um objeto "pegue emprestado" propriedades e comportamentos de outro. Isso evita repetição de código e ajuda a organizar melhor nossos programas. Em JavaScript, temos um jeito especial de fazer isso: herança prototipada.

💡 "Herança" é quando um objeto ou classe "filha" ganha tudo o que a classe "pai" tem — como poderes sendo passados de geração em geração! 🧙‍♂️➡️🧙‍♀️


🧱 Herança Prototipada: Como funciona?

No JavaScript, objetos herdam de outros objetos por meio de protótipos. Essa é a famosa cadeia de protótipos (prototype chain).

Quando tentamos acessar uma propriedade/método, o JS:

  1. Procura no próprio objeto.

  2. Se não encontrar, sobe para o protótipo (__proto__).

  3. Continua subindo na cadeia até encontrar ou chegar no null.

const animal = {
  tipo: "Animal",
  fazerSom() {
    console.log("Som genérico...");
  }
};

const cachorro = Object.create(animal);
cachorro.fazerSom(); // Som genérico...

🧠 Object.create() cria um novo objeto que herda de outro.


🧪 Exemplo com função construtora

📌 Herdando com e sem parâmetros

function Pessoa(nome) {
  this.nome = nome;
  this.falar = function () {
    console.log("Olá! Meu nome é " + this.nome);
  };
}

function Professor(nome, materia) {
  Pessoa.call(this, nome); // ⚠️ Chama o construtor pai (Pessoa) com o contexto do filho
  this.materia = materia;
}

🔁 Definindo o protótipo e a referência do construtor

Professor.prototype = Object.create(Pessoa.prototype);
Professor.prototype.constructor = Professor; // 🛠 Corrige o ponteiro do construtor

📎 Sobrescrevendo um método

Professor.prototype.falar = function () {
  console.log("Sou o professor " + this.nome + ", e ensino " + this.materia);
};

const prof = new Professor("Carlos", "Matemática");
prof.falar(); // "Sou o professor Carlos, e ensino Matemática"

⚠️ Importante: Sempre que usar Object.create() para herdar, lembre de corrigir a referência constructor.


🧠 Observação importante: cuidado com o Object.create() e o constructor!

Quando usamos Object.create() para criar herança entre objetos ou funções construtoras, estamos dizendo que o novo objeto deve herdar o protótipo de outro.

👀 Mas tem um detalhe importante que MUITA gente esquece:

O constructor do novo protótipo continua apontando para o construtor original (pai) — não para o filho! ⚠️

Vamos ver isso na prática:

function Pessoa(nome) {
  this.nome = nome;
}

function Professor(nome, materia) {
  Pessoa.call(this, nome);
  this.materia = materia;
}

// Herança
Professor.prototype = Object.create(Pessoa.prototype);

console.log(Professor.prototype.constructor); 
// 👉 Resultado: function Pessoa(nome) {...} ❌

➡️ Isso acontece porque Object.create(Pessoa.prototype) cria um novo objeto cujo __proto__ é Pessoa.prototype. Então, o constructor é herdado de lá também.


🛠️ Como corrigir?

Basta redefinir manualmente o constructor da função filha:

Professor.prototype.constructor = Professor;

Agora tudo fica certo:

console.log(Professor.prototype.constructor);
// 👉 Resultado: function Professor(nome, materia) {...} ✅

📌 Por que isso é importante?

O constructor é usado por ferramentas, IDEs e o próprio JavaScript para entender qual função criou um objeto. Se essa referência estiver errada, pode causar:

  • Confusão na hora de debugar 🔍

  • Problemas em frameworks que usam constructor automaticamente 🔧

  • Dificuldade na manutenção e leitura do código 😵‍💫


✅ Resumo rápido

Situação
O que acontece
Como resolver

Usou Object.create()

constructor aponta para o pai ❌

Redefina manualmente com Filho.prototype.constructor = Filho


🧠 Observação: Entendendo Professor.prototype, Professor.constructor e Professor.prototype.constructor

Essas três estruturas aparecem MUITO quando estamos lidando com herança e protótipos em JavaScript. Vamos entender cada uma delas de forma clara, com comparações e exemplos simples:


🧱 Professor.prototype

É o protótipo dos objetos que serão criados quando usamos new Professor(). Tudo o que colocamos aqui será compartilhado entre as instâncias criadas com essa função.

function Professor(nome) {
  this.nome = nome;
}
Professor.prototype.ensinar = function () {
  console.log("Ensinando...");
};

const prof = new Professor("Ana");
prof.ensinar(); // Ensinando...

🧾 Professor.constructor

Isso se refere ao construtor da função em si, ou seja, Function! Lembre-se: em JavaScript, funções também são objetos — e o construtor das funções é Function.

console.log(Professor.constructor === Function); // true ✅

🧬 Professor.prototype.constructor

Aqui está o ponto chave! Essa propriedade indica qual função construiu aquele protótipo.

Por padrão, ela aponta para a função construtora correta. Mas quando usamos Object.create() para fazer herança, ela pode acabar apontando para a função errada!

function Pessoa(nome) {
  this.nome = nome;
}
function Professor(nome, materia) {
  Pessoa.call(this, nome);
  this.materia = materia;
}

// Herança
Professor.prototype = Object.create(Pessoa.prototype);

console.log(Professor.prototype.constructor); 
// 👉 Resultado: Pessoa ❌

Professor.prototype.constructor = Professor; // Corrigindo ✅

console.log(Professor.prototype.constructor);
// 👉 Resultado: Professor ✅

🧪 Recapitulando com uma tabela:

Expressão
O que é?
Tipo/Retorno

Professor.prototype

Protótipo dos objetos Professor

Objeto

Professor.constructor

Quem criou a função Professor

Function

Professor.prototype.constructor

Quem criou o protótipo do Professor

Professor (ou incorretamente Pessoa)


⚠️ Atenção: Confundir constructor com prototype.constructor é um dos erros mais comuns entre quem está começando com herança em JS! 😅


✅ Dica prática

Sempre que fizer herança com Object.create(), verifique se prototype.constructor está apontando corretamente. Se não estiver, corrija manualmente:

Filho.prototype.constructor = Filho;

🔍 Observação: O que retorna quando usamos apenas Professor.prototype?

Quando você acessa Professor.prototype diretamente no seu código, está consultando o objeto protótipo associado à função construtora Professor.

📦 Em outras palavras:

console.log(Professor.prototype);

🟰 Retorna um objeto que servirá como base para todas as instâncias criadas com new Professor().


🧠 O que tem dentro desse objeto?

Por padrão, ele começa assim:

function Professor() {}

console.log(Professor.prototype);
// 👉 Resultado:
{
  constructor: ƒ Professor()
}

Ou seja, ele contém:

  • A propriedade constructor, que aponta de volta para a própria função Professor.


💡 Pra que serve esse objeto?

É nesse Professor.prototype que você pode adicionar métodos ou propriedades compartilhadas entre todas as instâncias da função.

Professor.prototype.darAula = function () {
  console.log("Dando aula!");
};

const prof1 = new Professor();
const prof2 = new Professor();

prof1.darAula(); // Dando aula!
prof2.darAula(); // Dando aula!

✅ Ambos os objetos compartilham o mesmo método sem duplicar o código na memória!


📌 Por que isso é importante?

Entender o que Professor.prototype retorna é essencial para:

  • Trabalhar com herança corretamente

  • Criar métodos otimizados

  • Entender como funciona o encadeamento de protótipos

  • Evitar confusão com __proto__ e prototype.constructor


🛑 Atenção

Professor.prototype não é o protótipo da função Professor. Ele é o protótipo que será usado pelas instâncias criadas com new Professor()!

🔁 Já o Professor.__proto__ (com dois underlines) aponta para Function.prototype, pois Professor é uma função.


✅ Resumo rápido

Expressão
Significado

Professor.prototype

Objeto usado como protótipo pelas instâncias criadas

new Professor().__proto__

Sempre é igual a Professor.prototype


📘 ECMAScript 2015 (ES6): Herança com classes

A sintaxe moderna com class facilita muito o uso da herança:

class Pessoa {
  constructor(nome) {
    this.nome = nome;
  }

  falar() {
    console.log(`Olá, meu nome é ${this.nome}`);
  }
}

class Professor extends Pessoa {
  constructor(nome, materia) {
    super(nome); // 👈 Chama o construtor da superclasse
    this.materia = materia;
  }

  falar() {
    console.log(`Sou o prof. ${this.nome} e ensino ${this.materia}`);
  }

  get especialidade() {
    return this.materia.toUpperCase();
  }

  set especialidade(novaMateria) {
    this.materia = novaMateria;
  }
}

const prof2 = new Professor("Joana", "Biologia");
prof2.falar(); // "Sou o prof. Joana e ensino Biologia"
console.log(prof2.especialidade); // BIOLOGIA
prof2.especialidade = "Química";
console.log(prof2.especialidade); // QUÍMICA

🔍 Como funciona por debaixo dos panos?

Mesmo com a sintaxe class, o JavaScript continua usando protótipos! A classe nada mais é que um açúcar sintático para facilitar a vida de quem programa.

console.log(Object.getPrototypeOf(Professor)); // [Function: Pessoa]
console.log(Object.getPrototypeOf(prof2)); // Pessoa { ... }

🧩 Onde posso definir propriedades e métodos?

Local
Como declarar
Quando usar

Dentro da função construtora

this.nome = "..."

Quando cada instância precisa de um valor diferente

No próprio construtor (estático)

MinhaClasse.algumaFuncao = function () {}

Quando o método é da classe, não do objeto

No protótipo

MinhaClasse.prototype.metodo = function () {}

Quando todas as instâncias compartilham o mesmo comportamento


🧭 Quando usar herança em JavaScript?

✔️ Use quando:

  • Vários objetos compartilham lógica comum.

  • Você quer reaproveitar métodos entre objetos semelhantes.

  • Precisa organizar melhor responsabilidades e comportamentos.

🚫 Evite quando:

  • Os objetos têm pouco em comum.

  • A herança deixa o código mais confuso do que ajuda.

  • Composição seria mais clara do que herança.

💬 Dica: Uma boa regra é "preferir composição à herança", mas usar herança quando o comportamento for realmente compartilhado e hierárquico!


✅ Resumo prático

Conceito
Exemplo
Significado

call()

Pai.call(this)

Chama o construtor pai no contexto do filho

Object.create()

Filho.prototype = Object.create(Pai.prototype)

Faz herança prototipada

constructor

Filho.prototype.constructor = Filho

Corrige a referência do construtor

extends

class Filho extends Pai {}

Herança com classes (ES6)

super()

super()

Chama o construtor da superclasse

get/set

get nome() {}

Criam propriedades especiais

prototype

Classe.prototype.metodo = ...

Define métodos herdáveis


📚 Herança é uma ferramenta poderosa, mas deve ser usada com cuidado e propósito. Aprender como o JavaScript lida com isso te transforma num programador ou programadora muito mais preparado(a)! 🚀

Last updated