Herança de Protótipos em JavaScript
JavaScript suporta herança, permitindo que criemos novos tipos de objetos que, quando necessário, herdem funcionalidades de objetos já existentes. No entanto, é importante entender que a herança em JavaScript difere da herança em outras linguagens populares como Java, C++, e C#. Em JavaScript, a herança é uma herança de objetos (e não de classes ou tipos), também conhecida como herança de protótipos ou herança prototípica.
Para criar um objeto baseado em um protótipo, utilizamos a função Object.create(), à qual passamos o protótipo a ser herdado:
const person = {
name: "",
age: 0,
print: function () {
console.log(`Name: ${this.name} Age: ${this.age}`);
},
};
const employee = Object.create(person); // employee utiliza o protótipo do objeto person
// obter o protótipo
console.log(employee.__proto__); // {name: "", age: 0, print: ƒ}
employee.name = "Tom";
employee.age = 39;
employee.print(); // Name: Tom Age: 39Neste caso, o objeto employee é criado com base no protótipo do objeto person, ou seja, employee herda o protótipo de person. Graças a essa herança, employee possui todas as propriedades e métodos definidos no objeto person.
Adicionalmente, objetos podem definir suas próprias propriedades e métodos. Por exemplo:
const person = {
name: "",
age: 0,
print: function () {
console.log(`Name: ${this.name} Age: ${this.age}`);
},
};
const employee = Object.create(person);
employee.name = "Tom";
employee.age = 39;
employee.company = "Google"; // nova propriedade
// novo método
employee.work = function () {
console.log(`${this.name} works at ${this.company}`);
};
employee.print(); // Name: Tom Age: 39
employee.work(); // Tom works at GoogleNeste caso, o objeto employee também define a propriedade company e o método work.
Quando necessário, podemos redefinir métodos herdados:
employee.print = function () {
console.log(`Name: ${this.name} Age: ${this.age} Company: ${this.company}`);
};
employee.print(); // Name: Tom Age: 39 Company: GoogleAqui, a função print é redefinida para também exibir a empresa do funcionário. Podemos estender a cadeia de herança ainda mais:
const person = {
name: "",
age: 0,
print: function () {
console.log(`Name: ${this.name} Age: ${this.age}`);
},
};
const employee = Object.create(person);
employee.company = "";
// objeto manager herda o protótipo do objeto employee
const manager = Object.create(employee);
// redefinindo o método print
manager.print = function () {
console.log(`Name: ${this.name} Age: ${this.age}\nManager at ${this.company}`);
};
manager.name = "Bob";
manager.age = 43;
manager.company = "Microsoft";
manager.print(); // Name: Bob Age: 43
// Manager at MicrosoftAssim, criamos uma cadeia de protótipos: person-employee-manager: employee herda o protótipo de person, e manager herda o protótipo de employee.
Chamada de Métodos do Protótipo Base
Às vezes, pode ser necessário chamar métodos definidos no protótipo. Isso pode ser útil para reduzir duplicação de código, especialmente quando o código do método redefinido repete a lógica do método no protótipo. Ao obter o protótipo de um objeto, podemos chamar seus métodos usando a função call():
employee.print = function () {
this.__proto__.print.call(this); // chama a versão do método de person
// Object.getPrototypeOf(this).print.call(this); // outra forma de chamar
console.log(`Company: ${this.company}`);
};
employee.print(); // Name: Tom Age: 39
// Company: GoogleNeste exemplo, no método print redefinido do tipo employee, chamamos através do protótipo a versão do método print de person.
Verificação de Herança de Protótipos e Object.isPrototypeOf()
Usamos o método Object.isPrototypeOf() para verificar se um objeto é protótipo de outro:
const person = {
name: "",
print: () => console.log("Name:", this.name),
};
const user = {
name: "",
print: () => console.log("Name:", this.name),
};
// o objeto employee herda o protótipo de person
const employee = Object.create(person);
console.log(person.isPrototypeOf(employee)); // true
console.log(user.isPrototypeOf(employee)); // falseAqui, o objeto employee herda o protótipo de person, então a chamada person.isPrototypeOf(employee) retorna verdadeiro, enquanto que user não é um protótipo de employee, apesar de ter o mesmo conjunto de métodos e propriedades.