POO
Classes e Objetos Modernos
JavaScript Moderno: Programação Orientada a Objetos
JavaScript é uma linguagem flexível que suporta vários paradigmas de programação, entre eles a programação orientada a objetos (POO). Desde o ES6, o JavaScript introduziu uma sintaxe de classe mais intuitiva, embora o modelo subjacente continue baseado em protótipos. Neste tutorial, vamos explorar a POO em JavaScript moderno, incluindo classes, herança, mixins e os padrões de projeto mais utilizados.
O modelo de objetos em JavaScript
Antes de explorar a sintaxe moderna de classes, é importante entender como funciona o modelo de objetos em JavaScript.
Objetos e protótipos
Em JavaScript, quase tudo é um objeto, e a herança funciona por meio de protótipos:
// Criação de um objeto literal
const personne = {
nom: "Alice",
saluer() {
return `Bonjour, je suis ${this.nom}`;
}
};
console.log(personne.saluer()); // "Bonjour, je suis Alice"
// Criação de um novo objeto baseado no protótipo de personne
const etudiant = Object.create(personne);
etudiant.nom = "Bob";
etudiant.niveau = "Master";
console.log(etudiant.saluer()); // "Bonjour, je suis Bob"
console.log(etudiant.niveau); // "Master"
Funções construtoras (método antigo)
Antes do ES6, a criação de objetos semelhantes era geralmente feita com funções construtoras:
// Função construtora
function Personne(nom, age) {
this.nom = nom;
this.age = age;
}
// Método adicionado ao protótipo
Personne.prototype.saluer = function() {
return `Bonjour, je suis ${this.nom} et j'ai ${this.age} ans`;
};
// Criação de instâncias
const alice = new Personne("Alice", 30);
const bob = new Personne("Bob", 25);
console.log(alice.saluer()); // "Bonjour, je suis Alice et j'ai 30 ans"
console.log(bob.saluer()); // "Bonjour, je suis Bob et j'ai 25 ans"
Classes em JavaScript moderno (ES6+)
O ES6 introduziu uma sintaxe de classe mais intuitiva, que simplifica a criação de objetos e a implementação de herança.
Definição de classe
class Personne {
// Construtor
constructor(nom, age) {
this.nom = nom;
this.age = age;
}
// Métodos
saluer() {
return `Bonjour, je suis ${this.nom} et j'ai ${this.age} ans`;
}
decrire() {
return `${this.nom} est une personne de ${this.age} ans`;
}
}
// Criação de instâncias
const alice = new Personne("Alice", 30);
console.log(alice.saluer()); // "Bonjour, je suis Alice et j'ai 30 ans"
Propriedades e métodos estáticos
Os métodos e propriedades estáticos pertencem à própria classe, não às instâncias:
class MathUtils {
// Propriedade estática (ES2022+)
static PI = 3.14159;
// Método estático
static carre(x) {
return x * x;
}
static aire_cercle(rayon) {
return MathUtils.PI * MathUtils.carre(rayon);
}
}
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.carre(4)); // 16
console.log(MathUtils.aire_cercle(2)); // 12.56636
// As propriedades e métodos estáticos não são acessíveis nas instâncias
const utils = new MathUtils();
// console.log(utils.PI); // undefined
Acessores (getters e setters)
Os acessores permitem controlar o acesso às propriedades de uma classe:
class Compte {
constructor(proprietaire) {
this.proprietaire = proprietaire;
this._solde = 0; // Convenção: _ indica uma propriedade "privada"
}
// Getter
get solde() {
return `${this._solde} €`;
}
// Setter
set solde(valeur) {
if (isNaN(valeur)) {
throw new Error("Le solde doit être un nombre");
}
this._solde = valeur;
}
// Métodos normais
deposer(montant) {
this.solde = this._solde + montant;
return this._solde;
}
retirer(montant) {
if (montant > this._solde) {
throw new Error("Fonds insuffisants");
}
this.solde = this._solde - montant;
return this._solde;
}
}
const compte = new Compte("Alice");
compte.deposer(1000);
console.log(compte.solde); // "1000 €"
compte.retirer(500);
console.log(compte.solde); // "500 €"
// compte.solde = "beaucoup"; // Erro: Le solde doit être un nombre
Campos de classe (Class Fields)
Os campos de classe, introduzidos em versões mais recentes do JavaScript, permitem declarar propriedades diretamente na classe:
class Produit {
// Campos públicos
nom;
prix;
// Campo privado (ECMAScript 2022+)
#stock = 0;
// Campo estático
static nbProduits = 0;
constructor(nom, prix, stock) {
this.nom = nom;
this.prix = prix;
this.#stock = stock;
Produit.nbProduits++;
}
get stock() {
return this.#stock;
}
set stock(valeur) {
if (valeur < 0) {
throw new Error("Le stock ne peut pas être négatif");
}
this.#stock = valeur;
}
vendre(quantite) {
if (quantite > this.#stock) {
throw new Error("Stock insuffisant");
}
this.#stock -= quantite;
return `${quantite} unité(s) de ${this.nom} vendue(s)`;
}
// Método privado (ECMAScript 2022+)
#miseAJourInterne() {
console.log("Mise à jour interne");
}
}
const p1 = new Produit("Téléphone", 499, 10);
console.log(p1.nom); // "Téléphone"
console.log(p1.stock); // 10
p1.stock = 15;
console.log(p1.stock); // 15
console.log(p1.vendre(3)); // "3 unité(s) de Téléphone vendue(s)"
console.log(p1.stock); // 12
// console.log(p1.#stock); // Erro: propriedade privada
// p1.#miseAJourInterne(); // Erro: método privado
console.log(Produit.nbProduits); // 1
Herança e polimorfismo
A herança permite que uma classe herde as propriedades e métodos de outra classe.
Herança com extends
class Animal {
constructor(nom) {
this.nom = nom;
}
parler() {
return `${this.nom} fait du bruit`;
}
}
class Chien extends Animal {
constructor(nom, race) {
super(nom); // Chamada do construtor pai
this.race = race;
}
// Sobrescrita do método parler
parler() {
return `${this.nom} aboie`;
}
decrire() {
return `${this.nom} est un chien de race ${this.race}`;
}
}
const animal = new Animal("Animal inconnu");
console.log(animal.parler()); // "Animal inconnu fait du bruit"
const rex = new Chien("Rex", "Berger Allemand");
console.log(rex.parler()); // "Rex aboie"
console.log(rex.decrire()); // "Rex est un chien de race Berger Allemand"
A cadeia de protótipos
Mesmo com a sintaxe moderna de classes, o JavaScript continua usando protótipos nos bastidores:
const rex = new Chien("Rex", "Berger Allemand");
console.log(rex instanceof Chien); // true
console.log(rex instanceof Animal); // true
console.log(rex instanceof Object); // true
console.log(Object.getPrototypeOf(rex) === Chien.prototype); // true
console.log(Object.getPrototypeOf(Chien.prototype) === Animal.prototype); // true
Verificação de tipo
Para verificar se um objeto é uma instância de uma classe, use o operador instanceof:
function traiterAnimal(animal) {
if (animal instanceof Chien) {
console.log(animal.parler()); // Método de Chien
} else if (animal instanceof Animal) {
console.log(animal.parler()); // Método de Animal
} else {
console.log("Ceci n'est pas un animal");
}
}
traiterAnimal(new Chien("Rex", "Berger Allemand")); // "Rex aboie"
traiterAnimal(new Animal("Lion")); // "Lion fait du bruit"
traiterAnimal({}); // "Ceci n'est pas un animal"
Composição e mixins
Às vezes, a herança sozinha não é suficiente para modelar relações complexas. A composição e os mixins oferecem alternativas flexíveis.
Composição
A composição consiste em combinar objetos mais simples para criar objetos mais complexos:
// Objetos de capacidades
const peutNager = {
nager() {
return `${this.nom} nage`;
}
};
const peutVoler = {
voler() {
return `${this.nom} vole`;
}
};
// Classe base
class Animal {
constructor(nom) {
this.nom = nom;
}
manger() {
return `${this.nom} mange`;
}
}
// Composição por mixin
class Canard extends Animal {
constructor(nom) {
super(nom);
// Composição: adicionar capacidades
Object.assign(this, peutNager, peutVoler);
}
}
class Poisson extends Animal {
constructor(nom) {
super(nom);
// Composição: apenas nadar
Object.assign(this, peutNager);
}
}
const donald = new Canard("Donald");
console.log(donald.manger()); // "Donald mange"
console.log(donald.nager()); // "Donald nage"
console.log(donald.voler()); // "Donald vole"
const nemo = new Poisson("Nemo");
console.log(nemo.nager()); // "Nemo nage"
// console.log(nemo.voler()); // Erro: nemo.voler n'est pas une fonction
Mixins funcionais
Os mixins funcionais são funções que adicionam propriedades e métodos a um objeto existente:
// Mixin funcional
function avecTimestamp(Base) {
return class extends Base {
constructor(...args) {
super(...args);
this.createdAt = new Date();
}
getAge() {
return new Date() - this.createdAt;
}
};
}
function avecLogging(Base) {
return class extends Base {
log(message) {
console.log(`[${this.constructor.name}] ${message}`);
}
};
}
// Aplicação dos mixins
class Utilisateur {
constructor(nom) {
this.nom = nom;
}
}
// Combinação de mixins
const UtilisateurAvecExtensions = avecLogging(avecTimestamp(Utilisateur));
const user = new UtilisateurAvecExtensions("Alice");
console.log(user.nom); // "Alice"
console.log(user.createdAt); // Data de criação
user.log("Instance créée"); // "[UtilisateurAvecExtensions] Instance créée"
Padrões de projeto em JavaScript
Os padrões de projeto (design patterns) são soluções reutilizáveis para problemas recorrentes. Veja alguns padrões comuns em JavaScript.
Singleton
O padrão Singleton garante que uma classe tenha apenas uma instância e fornece um ponto de acesso global a essa instância:
class Configuration {
constructor() {
if (Configuration._instance) {
return Configuration._instance;
}
this.theme = "clair";
this.langue = "fr";
this.notifications = true;
Configuration._instance = this;
}
static getInstance() {
return new Configuration();
}
}
const config1 = Configuration.getInstance();
const config2 = Configuration.getInstance();
console.log(config1 === config2); // true
config1.theme = "sombre";
console.log(config2.theme); // "sombre" (mesma instância)
Factory (Fábrica)
O padrão Factory cria objetos sem expor a lógica de criação:
// Classes concretas
class VoitureEconomique {
constructor(options) {
this.portes = options.portes || 4;
this.couleur = options.couleur || "blanche";
this.consommation = "faible";
}
}
class VoitureLuxe {
constructor(options) {
this.portes = options.portes || 2;
this.couleur = options.couleur || "noire";
this.consommation = "élevée";
this.options = ["cuir", "GPS", "toit ouvrant"];
}
}
class VoitureSport {
constructor(options) {
this.portes = 2;
this.couleur = options.couleur || "rouge";
this.consommation = "très élevée";
this.vitesseMax = 300;
}
}
// Factory
class VoitureFactory {
createVoiture(type, options) {
switch(type) {
case "economique":
return new VoitureEconomique(options);
case "luxe":
return new VoitureLuxe(options);
case "sport":
return new VoitureSport(options);
default:
throw new Error(`Type de voiture non reconnu: ${type}`);
}
}
}
// Utilização
const factory = new VoitureFactory();
const maVoiture = factory.createVoiture("luxe", { couleur: "bleu", portes: 4 });
console.log(maVoiture); // VoitureLuxe com opções personalizadas
Observer (Observador)
O padrão Observer permite que um objeto notifique outros objetos sobre mudanças de estado:
class Sujet {
constructor() {
this.observateurs = [];
}
souscrire(observateur) {
this.observateurs.push(observateur);
}
desinscrire(observateur) {
this.observateurs = this.observateurs.filter(obs => obs !== observateur);
}
notifier(donnees) {
this.observateurs.forEach(observateur => observateur.update(donnees));
}
}
class Observable extends Sujet {
constructor() {
super();
this.etat = null;
}
getEtat() {
return this.etat;
}
setEtat(etat) {
this.etat = etat;
this.notifier(this);
}
}
// Exemplo de uso: sistema de notificação meteorológica
class StationMeteo extends Observable {
setMesures(temperature, humidite, pression) {
this.etat = { temperature, humidite, pression, date: new Date() };
this.notifier(this);
}
}
// Observadores
class AffichageConditionsActuelles {
update(station) {
const { temperature, humidite } = station.getEtat();
console.log(`Conditions actuelles: ${temperature}°C et ${humidite}% d'humidité`);
}
}
class AffichageStatistiques {
constructor() {
this.temperatures = [];
}
update(station) {
const { temperature } = station.getEtat();
this.temperatures.push(temperature);
const max = Math.max(...this.temperatures);
const min = Math.min(...this.temperatures);
const moy = this.temperatures.reduce((acc, val) => acc + val, 0) / this.temperatures.length;
console.log(`Statistiques - Max: ${max}°C, Min: ${min}°C, Moyenne: ${moy.toFixed(1)}°C`);
}
}
// Utilização
const station = new StationMeteo();
const affichageConditions = new AffichageConditionsActuelles();
const affichageStats = new AffichageStatistiques();
station.souscrire(affichageConditions);
station.souscrire(affichageStats);
// Simula mudanças meteorológicas
station.setMesures(20, 65, 1013);
station.setMesures(22, 70, 1014);
station.setMesures(19, 75, 1012);
Module
O padrão Module encapsula funcionalidades e expõe uma API pública, mantendo alguns detalhes privados:
// Módulo com IIFE (estilo antigo)
const Compteur = (function() {
// Variáveis privadas
let compte = 0;
// Métodos privados
function valider(valeur) {
return typeof valeur === 'number' && !isNaN(valeur);
}
// API pública
return {
incrementer() {
return ++compte;
},
decrementer() {
return --compte;
},
ajouter(valeur) {
if (!valider(valeur)) {
throw new Error("Valeur invalide");
}
compte += valeur;
return compte;
},
getCompte() {
return compte;
}
};
})();
console.log(Compteur.getCompte()); // 0
Compteur.incrementer();
Compteur.incrementer();
console.log(Compteur.getCompte()); // 2
Compteur.ajouter(5);
console.log(Compteur.getCompte()); // 7
// console.log(Compteur.compte); // undefined (privado)
Decorator
O padrão Decorator permite adicionar comportamentos a objetos individuais sem afetar o comportamento de outros objetos da mesma classe:
// Componente base
class Cafe {
cout() {
return 2.5;
}
description() {
return "Café simple";
}
}
// Decoradores
class LaitDecorator {
constructor(cafe) {
this.cafe = cafe;
}
cout() {
return this.cafe.cout() + 0.5;
}
description() {
return `${this.cafe.description()} avec du lait`;
}
}
class SucreDecorator {
constructor(cafe) {
this.cafe = cafe;
}
cout() {
return this.cafe.cout() + 0.2;
}
description() {
return `${this.cafe.description()} avec du sucre`;
}
}
class CannelleDecorator {
constructor(cafe) {
this.cafe = cafe;
}
cout() {
return this.cafe.cout() + 0.3;
}
description() {
return `${this.cafe.description()} avec de la cannelle`;
}
}
// Uso
let monCafe = new Cafe();
console.log(`${monCafe.description()} : ${monCafe.cout()}€`);
// "Café simple : 2.5€"
// Adicionando leite
monCafe = new LaitDecorator(monCafe);
console.log(`${monCafe.description()} : ${monCafe.cout()}€`);
// "Café simple avec du lait : 3€"
// Adicionando açúcar
monCafe = new SucreDecorator(monCafe);
console.log(`${monCafe.description()} : ${monCafe.cout()}€`);
// "Café simple avec du lait avec du sucre : 3.2€"
// Criando diretamente um café personalizado
const cafeComplet = new CannelleDecorator(new SucreDecorator(new LaitDecorator(new Cafe())));
console.log(`${cafeComplet.description()} : ${cafeComplet.cout()}€`);
// "Café simple avec du lait avec du sucre avec de la cannelle : 3.5€"
Gerenciamento avançado de objetos
O JavaScript oferece várias formas avançadas de manipular e controlar objetos.
Object.defineProperty
Object.defineProperty permite definir propriedades com controle preciso sobre o comportamento delas:
const personne = {};
Object.defineProperty(personne, 'nom', {
value: 'Alice',
writable: true, // pode ser modificada
enumerable: true, // aparece nos laços for...in
configurable: true // pode ser removida
});
Object.defineProperty(personne, 'age', {
value: 30,
writable: false // não pode ser modificada
});
console.log(personne.nom); // "Alice"
personne.nom = "Bob";
console.log(personne.nom); // "Bob"
console.log(personne.age); // 30
personne.age = 40;
console.log(personne.age); // 30 (não modificada porque writable: false)
Proxy
Os Proxies permitem personalizar o comportamento das operações fundamentais sobre objetos:
const cible = {
message: "Hello",
prix: 100
};
const handler = {
// Interceptação do acesso às propriedades
get(cible, prop, receiver) {
console.log(`Accès à la propriété: ${prop}`);
if (prop === 'prix') {
return `${cible[prop]}€`;
}
return cible[prop];
},
// Interceptação da modificação de propriedades
set(cible, prop, valeur, receiver) {
console.log(`Modification de ${prop}: ${valeur}`);
if (prop === 'prix' && valeur < 0) {
throw new Error("Le prix ne peut pas être négatif");
}
cible[prop] = valeur;
return true; // sucesso
}
};
const proxy = new Proxy(cible, handler);
console.log(proxy.message); // Log: "Accès à la propriété: message" depois "Hello"
console.log(proxy.prix); // Log: "Accès à la propriété: prix" depois "100€"
proxy.message = "Bonjour"; // Log: "Modification de message: Bonjour"
// proxy.prix = -50; // Erro: Le prix ne peut pas être négatif
Símbolos e propriedades Well-Known
Os símbolos podem ser usados como chaves de propriedades únicas e não enumeráveis:
// Criação de um símbolo
const idSymbol = Symbol("id");
const utilisateur = {
nom: "Alice",
[idSymbol]: 12345 // propriedade baseada em um símbolo
};
console.log(utilisateur.nom); // "Alice"
console.log(utilisateur[idSymbol]); // 12345
// As propriedades baseadas em símbolos não são enumeráveis por padrão
for (let key in utilisateur) {
console.log(key); // Exibe apenas "nom", não o símbolo
}
// Símbolos well-known para personalizar o comportamento
const collectionPersonnalisee = {
elements: [1, 2, 3, 4, 5],
// Personalização da iteração
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.elements.length) {
return { value: this.elements[index++] * 2, done: false };
}
return { done: true };
}
};
}
};
// Uso com for...of, que utiliza o iterador
for (const item of collectionPersonnalisee) {
console.log(item); // 2, 4, 6, 8, 10
}
Exercícios práticos
Para solidificar seus conhecimentos, tente estes exercícios:
-
Sistema de formas: crie uma hierarquia de classes com uma classe
Formecomo base e subclasses comoCercle,RectangleeTriangle, cada uma com seus próprios métodos para calcular a área e o perímetro. -
Gerenciamento de biblioteca: implemente um sistema de gerenciamento de biblioteca com classes para
Livre,Auteur,BibliothèqueeEmprunteur. Use o encapsulamento para proteger os dados e os mixins para adicionar funcionalidades como o acompanhamento de empréstimos. -
Mini framework de UI: crie um pequeno framework de UI com uma classe
Componentcomo base e componentes específicos comoButton,InputePanelque herdam dessa base. Use métodos comorender()eupdate(). -
Implementação de padrões: implemente o padrão Singleton para um serviço de log e o padrão Factory para criar diferentes tipos de notificações (e-mail, SMS, push).
-
API de validação: crie uma API de validação com decoradores para validar as propriedades de um objeto (por exemplo, comprimento mínimo de uma string, intervalo de valores para um número).
Exemplo completo: sistema de gerenciamento de produtos
Aqui está um exemplo que combina vários conceitos abordados neste tutorial:
// Classe base
class Produit {
#id;
#nom;
#prix;
#stock;
static #compteur = 0;
constructor(nom, prix, stock = 0) {
this.#id = ++Produit.#compteur;
this.#nom = nom;
this.#prix = prix;
this.#stock = stock;
}
// Getters/Setters
get id() { return this.#id; }
get nom() { return this.#nom; }
set nom(value) { this.#nom = value; }
get prix() { return this.#prix; }
set prix(value) {
if (value < 0) throw new Error("Le prix ne peut pas être négatif");
this.#prix = value;
}
get stock() { return this.#stock; }
set stock(value) {
if (value < 0) throw new Error("Le stock ne peut pas être négatif");
this.#stock = value;
}
// Métodos
ajouter_stock(quantite) {
this.stock += quantite;
return this.stock;
}
retirer_stock(quantite) {
if (quantite > this.stock) throw new Error("Stock insuffisant");
this.stock -= quantite;
return this.stock;
}
toString() {
return `Produit #${this.id}: ${this.nom} - Prix: ${this.prix}€ - Stock: ${this.stock}`;
}
}
// Subclasse especializada
class ProduitPerissable extends Produit {
#dateExpiration;
constructor(nom, prix, stock, dateExpiration) {
super(nom, prix, stock);
this.#dateExpiration = new Date(dateExpiration);
}
get dateExpiration() { return this.#dateExpiration; }
estPerime() {
return new Date() > this.#dateExpiration;
}
toString() {
return `${super.toString()} - Expire le: ${this.#dateExpiration.toLocaleDateString()}`;
}
}
// Mixin para o acompanhamento de modificações
const avecHistorique = Base => class extends Base {
#historique = [];
constructor(...args) {
super(...args);
}
ajouterEvenement(type, details) {
const evenement = {
type,
details,
date: new Date()
};
this.#historique.push(evenement);
return evenement;
}
get historique() {
return [...this.#historique];
}
};
// Aplicação do mixin
class ProduitAvecHistorique extends avecHistorique(Produit) {
constructor(...args) {
super(...args);
this.ajouterEvenement("creation", { prix: args[1], stock: args[2] || 0 });
}
// Sobrescrita para adicionar eventos
ajouter_stock(quantite) {
const stockAvant = this.stock;
const nouveauStock = super.ajouter_stock(quantite);
this.ajouterEvenement("ajout_stock", {
avant: stockAvant,
ajout: quantite,
apres: nouveauStock
});
return nouveauStock;
}
retirer_stock(quantite) {
const stockAvant = this.stock;
const nouveauStock = super.retirer_stock(quantite);
this.ajouterEvenement("retrait_stock", {
avant: stockAvant,
retrait: quantite,
apres: nouveauStock
});
return nouveauStock;
}
set prix(value) {
const ancienPrix = this.prix;
super.prix = value;
this.ajouterEvenement("changement_prix", {
avant: ancienPrix,
apres: value
});
}
}
// Factory para criar diferentes tipos de produtos
class ProduitFactory {
creerProduit(type, ...args) {
switch(type) {
case "standard":
return new ProduitAvecHistorique(...args);
case "perissable":
return new ProduitPerissable(...args);
default:
throw new Error(`Type de produit inconnu: ${type}`);
}
}
}
// Gerenciador de catálogo (Singleton)
class CatalogueGestionnaire {
#produits = new Map();
static #instance;
constructor() {
if (CatalogueGestionnaire.#instance) {
return CatalogueGestionnaire.#instance;
}
CatalogueGestionnaire.#instance = this;
}
ajouterProduit(produit) {
this.#produits.set(produit.id, produit);
return produit;
}
getProduit(id) {
return this.#produits.get(id);
}
listerProduits() {
return [...this.#produits.values()];
}
listerProduitsEnStock() {
return this.listerProduits().filter(p => p.stock > 0);
}
}
// Demonstração
function demoProduits() {
// Criação de produtos com Factory
const factory = new ProduitFactory();
const p1 = factory.creerProduit("standard", "Clavier", 49.99, 10);
const p2 = factory.creerProduit("standard", "Souris", 29.99, 15);
const p3 = factory.creerProduit("perissable", "Yaourt", 2.99, 50, "2023-12-31");
// Uso do singleton
const catalogue = new CatalogueGestionnaire();
catalogue.ajouterProduit(p1);
catalogue.ajouterProduit(p2);
catalogue.ajouterProduit(p3);
// Operações
console.log("Produits en catalogue:");
catalogue.listerProduits().forEach(p => console.log(` - ${p}`));
p1.ajouter_stock(5);
p2.retirer_stock(3);
p1.prix = 45.99;
console.log("\nHistorique du clavier:");
p1.historique.forEach((e, i) => {
console.log(`${i+1}. [${e.date.toLocaleTimeString()}] ${e.type}: `, e.details);
});
console.log("\nTest d'un produit périssable:");
console.log(p3.toString());
console.log(`Est périmé: ${p3.estPerime()}`);
}
// Execução
demoProduits();
Conclusão
Neste tutorial, exploramos a programação orientada a objetos no JavaScript moderno:
- A sintaxe moderna de classes e seus recursos (construtores, getters/setters, métodos estáticos, campos privados)
- A herança e o polimorfismo por meio de
extendse da sobrescrita de métodos - A composição e os mixins para uma reutilização flexível de código
- Padrões de projeto comuns, como Singleton, Factory, Observer e Decorator
- Técnicas avançadas de manipulação de objetos com
Object.definePropertyeProxy
O JavaScript oferece uma abordagem flexível de POO, que combina elementos de programação baseada em protótipos com uma sintaxe mais familiar, inspirada nas linguagens orientadas a objetos clássicas. Essa flexibilidade permite escolher o estilo que melhor atende às suas necessidades, seja a herança clássica, a composição ou uma abordagem mais funcional.
No próximo tutorial, vamos explorar as Promises e o async/await para o gerenciamento de código assíncrono.
Precisa revisar o destructuring? Confira nosso tutorial anterior sobre destructuring.
Pronto para continuar? Passe para o próximo tutorial sobre Promises e async/await.
Comentários
Os comentários são alimentados pelo GitHub Discussions
Conecte-se com o GitHub para participar da discussão