Pular para o conteúdo principal

Campos de classe públicos e privados

· Leitura de 5 minutos
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Várias propostas expandem a sintaxe de classes JavaScript existente com novas funcionalidades. Este artigo explica a nova sintaxe de campos de classe públicos no V8 v7.2 e Chrome 72, assim como a futura sintaxe de campos de classe privados.

Aqui está um exemplo de código que cria uma instância de uma classe chamada IncreasingCounter:

const counter = new IncreasingCounter();
counter.value;
// registra 'Obtendo o valor atual!'
// → 0
counter.increment();
counter.value;
// registra 'Obtendo o valor atual!'
// → 1

Note que acessar o value executa algum código (isto é, registra uma mensagem) antes de retornar o resultado. Agora pergunte-se, como você implementaria esta classe em JavaScript? 🤔

Sintaxe de classe ES2015

Veja como IncreasingCounter poderia ser implementado usando a sintaxe de classe ES2015:

class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('Obtendo o valor atual!');
return this._count;
}
increment() {
this._count++;
}
}

A classe instala o getter value e um método increment no protótipo. Mais interessantemente, a classe tem um construtor que cria uma propriedade de instância _count e define seu valor padrão como 0. Atualmente, costumamos usar o prefixo de sublinhado para indicar que _count não deve ser usado diretamente pelos consumidores da classe, mas isso é apenas uma convenção; não é realmente uma propriedade “privada” com semântica especial aplicada pela linguagem.

const counter = new IncreasingCounter();
counter.value;
// registra 'Obtendo o valor atual!'
// → 0

// Nada impede que as pessoas leiam ou alterem a
// propriedade de instância `_count`. 😢
counter._count;
// → 0
counter._count = 42;
counter.value;
// registra 'Obtendo o valor atual!'
// → 42

Campos de classe públicos

A nova sintaxe de campos de classe públicos nos permite simplificar a definição da classe:

class IncreasingCounter {
_count = 0;
get value() {
console.log('Obtendo o valor atual!');
return this._count;
}
increment() {
this._count++;
}
}

A propriedade _count agora está declarada de maneira organizada no início da classe. Não precisamos mais de um construtor apenas para definir alguns campos. Legal!

No entanto, o campo _count ainda é uma propriedade pública. Neste exemplo específico, queremos impedir que as pessoas acessem diretamente a propriedade.

Campos de classe privados

É aqui que os campos de classe privados entram. A nova sintaxe de campos privados é semelhante aos campos públicos, exceto que você marca o campo como privado usando #. Você pode pensar no # como parte do nome do campo:

class IncreasingCounter {
#count = 0;
get value() {
console.log('Obtendo o valor atual!');
return this.#count;
}
increment() {
this.#count++;
}
}

Campos privados não são acessíveis fora do corpo da classe:

const counter = new IncreasingCounter();
counter.#count;
// → SyntaxError
counter.#count = 42;
// → SyntaxError

Propriedades estáticas públicas e privadas

A sintaxe de campos de classe pode ser usada para criar propriedades e métodos estáticos públicos e privados também:

class FakeMath {
// `PI` é uma propriedade estática pública.
static PI = 22 / 7; // Bem próximo.

// `#totallyRandomNumber` é uma propriedade estática privada.
static #totallyRandomNumber = 4;

// `#computeRandomNumber` é um método estático privado.
static #computeRandomNumber() {
return FakeMath.#totallyRandomNumber;
}

// `random` é um método estático público (sintaxe ES2015)
// que consome `#computeRandomNumber`.
static random() {
console.log('Ouvi dizer que você gosta de números aleatórios…');
return FakeMath.#computeRandomNumber();
}
}

FakeMath.PI;
// → 3.142857142857143
FakeMath.random();
// registra 'Ouvi dizer que você gosta de números aleatórios…'
// → 4
FakeMath.#totallyRandomNumber;
// → SyntaxError
FakeMath.#computeRandomNumber();
// → SyntaxError

Subclassificação mais simples

Os benefícios da sintaxe de campos de classe tornam-se ainda mais claros ao lidar com subclasses que introduzem campos adicionais. Imagine a seguinte classe base Animal:

class Animal {
constructor(name) {
this.name = name;
}
}

Para criar uma subclasse Cat que introduz uma propriedade de instância adicional, anteriormente você teria que chamar super() para executar o construtor da classe base Animal antes de criar a propriedade:

class Cat extends Animal {
constructor(name) {
super(name);
this.likesBaths = false;
}
meow() {
console.log('Miau!');
}
}

Isso é muito código boilerplate apenas para indicar que gatos não gostam de tomar banho. Felizmente, a sintaxe de campos de classe elimina a necessidade de todo o construtor, incluindo a chamada estranha de super():

class Cat extends Animal {
likesBaths = false;
meow() {
console.log('Miau!');
}
}

Suporte ao recurso

Suporte para campos de classe públicos

Suporte para campos de classe privados

Suporte para métodos privados e acessores