Em JavaScript, objetos possuem características únicas, mas quando uma propriedade específica não é encontrada, o objeto busca essa propriedade em outro objeto referenciado pelo seu protótipo. Essa funcionalidade é essencial para entender como a herança e o compartilhamento de funcionalidades funcionam na linguagem. Vamos explorar como essa busca ocorre e como otimizar o uso de Protótipos em JavaScript para economizar memória e evitar duplicação de código.
Objetos possuem uma propriedade interna e invisível chamada [[Prototype]], que aponta para outro objeto. Essa ligação permite que um objeto herde propriedades e métodos de seu protótipo, criando uma cadeia de herança.
Um exemplo prático dessa funcionalidade é o seguinte código:
let animal = {
eats: true,
walk() {
alert("Animal walk");
}
};
let rabbit = {
jumps: true,
__proto__: animal
};
// walk is taken from the prototype
rabbit.walk(); // Animal walk
Neste caso, o objeto rabbit herda do objeto animal, permitindo que rabbit utilize o método walk definido em animal.
## Cadeia de Protótipos em JavaScript
Quando tentamos acessar uma propriedade de um objeto que não existe, o JavaScript continua procurando essa propriedade na cadeia de protótipos. Esse comportamento pode ser estendido ainda mais, como no exemplo abaixo:
let animal = {
eats: true,
walk() {
alert("Animal walk");
}
};
let rabbit = {
jumps: true,
__proto__: animal
};
let longEar = {
earLength: 10,
__proto__: rabbit
};
// walk is taken from the prototype chain
longEar.walk(); // Animal walk
alert(longEar.jumps); // true (from rabbit)
Aqui, o objeto longEar herda de rabbit, que por sua vez herda de animal. Assim, longEar pode acessar tanto walk (de animal) quanto jumps (de rabbit).
### A Importância do this
O uso da palavra-chave this é crucial para entender como os protótipos funcionam em JavaScript. Veja este exemplo:
let user = {
name: "John",
surname: "Smith",
set fullName(value) {
[this.name, this.surname] = value.split(" ");
},
get fullName() {
return `${this.name} ${this.surname}`;
}
};
let admin = {
__proto__: user,
isAdmin: true
};
alert(admin.fullName); // John Smith
admin.fullName = "Alice Cooper";
alert(admin.fullName); // Alice Cooper, state of admin modified alert(user.fullName); // John Smith, state of user protected
Neste caso, this sempre se refere ao objeto antes do ponto quando uma propriedade ou método é acessado. Em admin.fullName, this se refere a admin, e os getters e setters de fullName operam nas propriedades de admin.
Mesmo que fullName seja herdado de user, a lógica no getter/setter se aplica ao objeto (admin) que o invocou. Isso significa que podemos personalizar o comportamento de objetos derivados sem modificar o objeto original.
### Alternativa ao __proto__: Object.create
O uso de __proto__ é desencorajado no JavaScript moderno devido a preocupações de desempenho e possíveis problemas. A mesma funcionalidade pode ser alcançada utilizando Object.create, que oferece uma maneira mais eficiente e clara de estabelecer a relação de protótipo.
let user = {
name: "John",
surname: "Smith",
set fullName(value) {
[this.name, this.surname] = value.split(" ");
},
get fullName() {
return `${this.name} ${this.surname}`;
}
};
let admin = Object.create(user);
// Output logged to the console
console.log(admin.fullName); // John Smith
admin.fullName = "Alice Cooper";
console.log(admin.fullName); // Alice Cooper
console.log(user.fullName); // John Smith
Utilizando Object.create, criamos um novo objeto admin que herda as propriedades e métodos de user, sem modificar o objeto original.
## Protótipos embutidos e Wrappers
JavaScript possui objetos embutidos como Array, Function e Map. Esses objetos armazenam seus métodos em seus protótipos, como slice() para arrays e size() para maps. Quando criamos um novo objeto usando new Object() ou let obj = {}, o [[Prototype]] é automaticamente definido como Object.prototype.
Todos os objetos embutidos herdam, em última instância, de Object prototype.
É importante notar que, embora JavaScript tenha objetos wrapper para primitivos como Number, String e Boolean, os valores null e undefined não possuem esses objetos. Esses wrappers permitem que os primitivos acessem métodos como se fossem objetos, mas desaparecem logo após o uso.
### Limitações e Estruturas Lineares
Objetos em JavaScript não apenas armazenam suas próprias propriedades, mas também possuem protótipos. Os protótipos permitem que objetos herdem propriedades e métodos de outros objetos, o que ajuda a economizar memória compartilhando funcionalidades em vez de duplicá-las.
Uma limitação importante é que os objetos não podem referenciar-se circularmente ou referenciar dois objetos simultaneamente na cadeia de protótipos. Por exemplo, a seguinte estrutura não é permitida: B <-- A --> C. A estrutura deve sempre seguir uma linha linear, como A –> B –> C.
Em resumo, os protótipos em JavaScript são um mecanismo poderoso para herança e compartilhamento de funcionalidades, permitindo que os desenvolvedores criem código mais eficiente e reutilizável. Compreender como funcionam os protótipos e como utilizá-los corretamente é fundamental para escrever código JavaScript de alta qualidade.
Este conteúdo foi auxiliado por Inteligência Artificiado, mas escrito e revisado por um humano.