O JavaScript é uma linguagem baseada em protótipo, e cada objeto no JavaScript tem uma propriedade interna escondida chamada [[Prototype]]
, que pode ser usada para estender as propriedades e métodos de objetos. Você pode ler mais sobre protótipos no nosso tutorial Entendendo protótipos e herança no JavaScript.
Até recentemente, os desenvolvedores criativos usavam funções de construção para imitar um padrão de design orientado a objeto no JavaScript. A especificação de linguagem ECMAScript 2015, frequentemente chamada de ES6, introduziu classes na linguagem JavaScript. As classes no JavaScript não oferecem, de fato, funcionalidades adicionais e são muitas vezes descritas como provedoras de “açúcar sintático” em relação a protótipos e herança, sendo que estes oferecem uma sintaxe mais limpa e mais elegante. Uma vez que outras linguagens de programação usam classes, a sintaxe de classe no JavaScript torna a coisa mais simples para que desenvolvedores consigam transitar entre linguagens.
Uma classe do JavaScript é um tipo de função. As classes são declaradas com a palavra-chave class
. Vamos usar a sintaxe de expressão de função para inicializar uma função e a sintaxe de expressão de classe para inicializar uma classe.
// Initializing a function with a function expression
const x = function() {}
// Initializing a class with a class expression
const y = class {}
Podemos acessar o [[Prototype]]
de um objeto usando o método Object.getPrototypeOf()
. Vamos usar isso para testar a função vazia que criamos.
Object.getPrototypeOf(x);
Outputƒ () { [native code] }
Também podemos usar esse método na classe que acabamos de criar.
Object.getPrototypeOf(y);
Outputƒ () { [native code] }
Ambos os código declarados com function
e class
retornam uma função [[Prototype]]
. Com protótipos, qualquer função pode se tornar uma instância de construção usando a palavra-chave new
.
const x = function() {}
// Initialize a constructor from a function
const constructorFromFunction = new x();
console.log(constructorFromFunction);
Outputx {}
constructor: ƒ ()
Isso também se aplica às classes.
const y = class {}
// Initialize a constructor from a class
const constructorFromClass = new y();
console.log(constructorFromClass);
Outputy {}
constructor: class
Estes exemplos de construtores de protótipo estão aparentemente vazios, mas podemos ver que sob a sintaxe, ambos os métodos estão alcançando o mesmo resultado final.
No tutorial de protótipos e herança, criamos um exemplo baseado na criação de personagens em um jogo RPG baseado em texto. Vamos continuar com esse exemplo para atualizar a sintaxe de funções para classes.
Uma função de construção é inicializada com um número de parâmetros que seriam atribuídos como propriedades de this
, referindo-se à função em si. A primeira letra do identificador seria maiúscula por convenção.
// Initializing a constructor function
function Hero(name, level) {
this.name = name;
this.level = level;
}
Quando traduzimos isso para a sintaxe classe mostrada abaixo, vemos que ela está estruturada de maneira similar.
// Initializing a class definition
class Hero {
constructor(name, level) {
this.name = name;
this.level = level;
}
}
Sabemos que uma função de construção é destinada a ser um projeto de objeto pela primeira letra do inicializador ser maiúscula (o que é opcional) e através da familiaridade com a sintaxe. A palavra-chave class
comunica de maneira mais simples o objetivo da nossa função.
A única diferença na sintaxe de inicialização é usar a palavra-chave class
ao invés de function
, e atribuir as propriedades dentro de um método constructor()
.
Uma prática comum com funções de construção é atribuir métodos diretamente ao prototype
ao invés da inicialização, como visto no método greet()
abaixo.
function Hero(name, level) {
this.name = name;
this.level = level;
}
// Adding a method to the constructor
Hero.prototype.greet = function() {
return `${this.name} says hello.`;
}
Com classes, esta sintaxe é simplificada e o método pode ser adicionado diretamente à classe. Ao usar a forma abreviada da definição do método introduzida como sendo ES6, definir um método é um processo ainda mais conciso.
class Hero {
constructor(name, level) {
this.name = name;
this.level = level;
}
// Adding a method to the constructor
greet() {
return `${this.name} says hello.`;
}
}
Vamos ver essas propriedades e métodos em ação. Criaremos uma nova instância Hero
usando a palavra-chave new
e atribuiremos alguns valores.
const hero1 = new Hero('Varg', 1);
Se imprimirmos mais informações sobre nosso novo objeto com console.log(hero1)
, podemos ver mais detalhes sobre o que está acontecendo com a inicialização da classe.
OutputHero {name: "Varg", level: 1}
__proto__:
▶ constructor: class Hero
▶ greet: ƒ greet()
Podemos ver no resultado que as funções constructor()
e greet()
foram aplicadas ao __proto__
, ou [[Prototype]]
do hero1
, e não diretamente como um método no objeto hero1
. Embora isso seja claro ao criar funções de construção, não é óbvio ao criar classes. As classes permitem uma sintaxe mais simples e sucinta, mas sacrifica um pouco de clareza no processo.
Uma característica vantajosa de funções de construção e classes é que elas podem ser estendidas para novos projetos de objeto baseados no pai. Isso impede a repetição de código para objetos semelhantes, mas precisa de algumas características adicionais ou mais específicas.
Novas funções de construção podem ser criadas a partir do pai usando o método call()
. No exemplo abaixo, vamos criar uma classe de personagens mais específica chamada Mage
e atribuir as propriedades de Hero
a ela usando o call()
, assim como adicionar uma propriedade adicional.
// Creating a new constructor from the parent
function Mage(name, level, spell) {
// Chain constructor with call
Hero.call(this, name, level);
this.spell = spell;
}
Neste ponto, podemos criar uma nova instância de Mage
usando as mesmas propriedades que o Hero
assim como uma nova que adicionamos.
const hero2 = new Mage('Lejon', 2, 'Magic Missile');
Ao enviar hero2
para o console, podemos ver que criamos um novo Mage
baseado no construtor.
OutputMage {name: "Lejon", level: 2, spell: "Magic Missile"}
__proto__:
▶ constructor: ƒ Mage(name, level, spell)
Com classes ES6, a palavra-chave super
é usada no lugar de call
para acessar as funções do pai. Vamos usar extends
para nos referir à classe pai.
// Creating a new class from the parent
class Mage extends Hero {
constructor(name, level, spell) {
// Chain constructor with super
super(name, level);
// Add a new property
this.spell = spell;
}
}
Agora, podemos criar uma nova instância Mage
da mesma maneira.
const hero2 = new Mage('Lejon', 2, 'Magic Missile');
Vamos imprimir hero2
para o console e visualizar o resultado.
OutputMage {name: "Lejon", level: 2, spell: "Magic Missile"}
__proto__: Hero
▶ constructor: class Mage
O resultado é quase exatamente o mesmo, exceto que na construção de classes o [[Prototype]]
está ligado ao pai, neste caso, Hero
.
Abaixo está uma comparação lado a lado do processo inteiro de inicialização, adição de métodos e herança de uma função de construção e uma classe.
function Hero(name, level) {
this.name = name;
this.level = level;
}
// Adding a method to the constructor
Hero.prototype.greet = function() {
return `${this.name} says hello.`;
}
// Creating a new constructor from the parent
function Mage(name, level, spell) {
// Chain constructor with call
Hero.call(this, name, level);
this.spell = spell;
}
// Initializing a class
class Hero {
constructor(name, level) {
this.name = name;
this.level = level;
}
// Adding a method to the constructor
greet() {
return `${this.name} says hello.`;
}
}
// Creating a new class from the parent
class Mage extends Hero {
constructor(name, level, spell) {
// Chain constructor with super
super(name, level);
// Add a new property
this.spell = spell;
}
}
Embora a sintaxe seja bastante diferente, o resultado fundamental é quase idêntico entre ambos os métodos. As classes dão-nos uma maneira mais concisa de criar plantas de objetos e as funções de construção descrevem com maior precisão o que está acontecendo nas entrelinhas.
Neste tutorial, aprendemos sobre as semelhanças e diferenças entre funções de construção e classes ES6 do JavaScript. Ambas classes e funções de construção imitam um modelo de herança orientado a objeto para JavaScript, que é uma linguagem de herança baseada em protótipo.
Compreender a herança prototípica é fundamental para ser um desenvolvedor eficaz do JavaScript. Estar familiarizado com classes é extremamente útil, já que as bibliotecas populares do JavaScript como a React fazem uso frequente da sintaxe class
.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!