Zephyrnet Logo

Novos campos de classe privada do JavaScript e como usá-los

Data:

O ES6 introduziu classes no JavaScript, mas elas podem ser simplistas demais para aplicativos complexos. Campos de classe (também chamados de propriedades de classe) visam fornecer construtores mais simples com membros privados e estáticos. A proposta é atualmente uma TC39, estágio 3: candidato e provavelmente será adicionado ao ES2019 (ES10). Atualmente, os campos privados são suportados no Node.js 12, Chrome 74 e Babel.

Uma rápida recapitulação das classes ES6 é útil antes de vermos como os campos de classe são implementados.

Este artigo foi atualizado em 2020. Para um conhecimento mais profundo sobre JavaScript, leia nosso livro, JavaScript: Iniciante em Ninja, 2ª Edição.

Noções básicas da classe ES6

O modelo de herança orientada a objetos do JavaScript pode confundir desenvolvedores provenientes de linguagens como C ++, C #, Java e PHP. Por esse motivo, o ES6 introduziu aulas. Eles são basicamente açúcar sintático, mas oferecem conceitos de programação orientados a objetos mais familiares.

Uma classe é uma modelo de objeto que define como os objetos desse tipo se comportam. Os seguintes Animal A classe define animais genéricos (as classes são normalmente indicadas com um capital inicial para distingui-los de objetos e outros tipos):

class Animal { constructor(name = 'anonymous', legs = 4, noise = 'nothing') { this.type = 'animal'; this.name = name; this.legs = legs; this.noise = noise; } speak() { console.log(`${this.name} says "${this.noise}"`); } walk() { console.log(`${this.name} walks on ${this.legs} legs`); } }

As declarações de classe sempre são executadas no modo estrito. Não há necessidade de adicionar 'use strict'.

A construtor O método é executado quando um objeto do Animal O tipo é criado. Geralmente define propriedades iniciais e lida com outras inicializações. speak() e walk() são métodos de instância que adicionam funcionalidade adicional.

Um objeto agora pode ser criado a partir dessa classe com o new palavra-chave:

let rex = new Animal('Rex', 4, 'woof');
rex.speak(); // Rex says "woof"
rex.noise = 'growl';
rex.speak(); // Rex says "growl"

Getters e Setters

Setters são métodos especiais usados ​​para definir apenas valores. Similarmente, Getters são métodos especiais usados ​​para retornar apenas um valor. Por exemplo:

class Animal { constructor(name = 'anonymous', legs = 4, noise = 'nothing') { this.type = 'animal'; this.name = name; this.legs = legs; this.noise = noise; } speak() { console.log(`${this.name} says "${this.noise}"`); } walk() { console.log(`${this.name} walks on ${this.legs} legs`); } // setter set eats(food) { this.food = food; } // getter get dinner() { return `${this.name} eats ${this.food || 'nothing'} for dinner.`; } } let rex = new Animal('Rex', 4, 'woof');
rex.eats = 'anything';
console.log( rex.dinner ); // Rex eats anything for dinner.

Criança ou subclasse

Muitas vezes, é prático usar uma classe como base para outra. UMA Human A classe pode herdar todas as propriedades e métodos do Animal classe usando o extends palavra-chave Propriedades e métodos podem ser adicionados, removidos ou alterados conforme necessário para que a criação de objetos humanos se torne mais fácil e mais legível:

class Human extends Animal { constructor(name) { // call the Animal constructor super(name, 2, 'nothing of interest'); this.type = 'human'; } // override Animal.speak speak(to) { super.speak(); if (to) console.log(`to ${to}`); } }

super refere-se à classe pai, por isso geralmente é a primeira chamada feita no constructor. Neste exemplo, o ser humano speak() O método substitui o definido em Animal.

Instâncias de objeto de Human agora pode ser criado:

let don = new Human('Don');
don.speak('anyone'); // Don says "nothing of interest" to anyone don.eats = 'burgers';
console.log( don.dinner ); // Don eats burgers for dinner.

Métodos e propriedades estáticas

Definindo um método com o static A palavra-chave permite que ela seja chamada em uma classe sem criar uma instância de objeto. Considere o Math.PI constante: não há necessidade de criar um Math objeto antes de acessar o PI propriedade.

O ES6 não suporta propriedades estáticas da mesma maneira que outros idiomas, mas é possível adicionar propriedades à própria definição de classe. Por exemplo, o Human A classe pode ser adaptada para manter uma contagem de quantos objetos humanos foram criados:

class Human extends Animal { constructor(name) { // call the Animal constructor super(name, 2, 'nothing of interest'); this.type = 'human'; // update count of Human objects Human.count++; } // override Animal.speak speak(to) { super.speak(); if (to) console.log(`to ${to}`); } // return number of human objects static get COUNT() { return Human.count; } } // static property of the class itself - not its objects
Human.count = 0;

A estática da classe COUNT getter retorna o número de humanos de acordo:

console.log(`Humans defined: ${Human.COUNT}`); // Humans defined: 0 let don = new Human('Don'); console.log(`Humans defined: ${Human.COUNT}`); // Humans defined: 1 let kim = new Human('Kim'); console.log(`Humans defined: ${Human.COUNT}`); // Humans defined: 2

Campos da classe ES2019 (NOVO)

A nova implementação de campos de classe permite que propriedades públicas sejam inicializadas na parte superior de uma classe fora de qualquer construtor:

class MyClass { a = 1; b = 2; c = 3; }

Isso é equivalente a:

class MyClass { constructor() { this.a = 1; this.b = 2; this.c = 3; } }

Se você ainda precisar de um construtor, os inicializadores serão executados antes de serem executados.

Campos de classe estática

No exemplo acima, as propriedades estáticas foram adicionadas de maneira deselegante ao objeto de definição de classe após sua definição. Isso não é necessário nos campos de classe:

class MyClass { x = 1; y = 2; static z = 3; } console.log( MyClass.z ); // 3

Isso é equivalente a:

class MyClass { constructor() { this.x = 1; this.y = 2; } } MyClass.z = 3; console.log( MyClass.z ); // 3

Campos de classe privada

Todas as propriedades nas classes ES6 são públicas por padrão e podem ser examinadas ou modificadas lado de fora a classe. No Animal exemplos acima, não há nada para impedir a food propriedade sendo alterada sem chamar o eats normatizador:

class Animal { constructor(name = 'anonymous', legs = 4, noise = 'nothing') { this.type = 'animal'; this.name = name; this.legs = legs; this.noise = noise; } set eats(food) { this.food = food; } get dinner() { return `${this.name} eats ${this.food || 'nothing'} for dinner.`; } } let rex = new Animal('Rex', 4, 'woof');
rex.eats = 'anything'; // standard setter
rex.food = 'tofu'; // bypass the eats setter altogether
console.log( rex.dinner ); // Rex eats tofu for dinner.

Outros idiomas geralmente permitem private propriedades a serem declaradas. Isso não é possível no ES6; portanto, os desenvolvedores geralmente trabalham com ele usando a convenção de sublinhado (_propertyName), fechamentos, símbolos ou WeakMaps. Um sublinhado fornece uma dica para o desenvolvedor, mas não há nada para impedi-lo de acessar essa propriedade.

No ES2019, os campos de classe privada são definidos usando um hash # prefixo:

class MyClass { a = 1; // .a is public #b = 2; // .#b is private static #c = 3; // .#c is private and static incB() { this.#b++; } } let m = new MyClass(); m.incB(); // runs OK
m.#b = 0; // error - private property cannot be modified outside class

Observe que não há como definir métodos privados, getters ou setters. UMA TC39, fase 3: projeto de proposta sugere usar um hash # prefixo em nomes e foi implementado em Babel. Por exemplo:

class MyClass { // private property #x = 0; // private method (can only be called within the class) #incX() { this.#x++; } // private setter (can only be used within the class) set #setX(x) { this.#x = x; } // private getter (can only be used within the class) get #getX() { return this.$x; } }

Componentes de reação geralmente têm métodos vinculados a eventos DOM. Para garantir this resolver para o componente, é necessário bind todo método em conformidade. Por exemplo:

class App extends Component { constructor() { super(); this.state = { count: 0 }; // bind all methods this.incCount = this.incCount.bind(this); } incCount() { this.setState(ps => { count: ps.count + 1 }) } render() { return ( <div> <p>{ this.state.count }</p> <button onClick={this.incCount}>add one</button> </div> ); }
}

Quando incCount é definido como um campo de classe ES2019, ele pode ser atribuído como uma função usando o ES6 => seta gorda, que é automaticamente vinculada ao objeto de definição. Não é mais necessário adicionar bind declarações:

class App extends Component { state = { count: 0 }; incCount = () => { this.setState(ps => { count: ps.count + 1 }) }; render() { return ( <div> <p>{ this.state.count }</p> <button onClick={this.incCount}>add one</button> </div> ); }
}

Campos de classe: uma melhoria?

As definições de classe ES6 eram simplistas. Os campos de classe do ES2019 requerem menos código, ajudam na legibilidade e permitem algumas possibilidades interessantes de programação orientada a objetos.

utilização # denotar privacidade recebeu algumas críticas, principalmente porque é feio e parece um hack. A maioria dos idiomas implementa um private palavra-chave, portanto, a tentativa de usar esse membro fora da classe será rejeitada pelo compilador.

JavaScript é interpretado. Considere o seguinte código:

class MyClass { private secret = 123;
} const myObject = new MyClass();
myObject.secret = 'one-two-three';

Isso geraria um erro de tempo de execução na última linha, mas isso é uma conseqüência grave para simplesmente tentar definir uma propriedade. O JavaScript perdoa propositadamente e a modificação de propriedade permitida pelo ES5 em qualquer objeto.

Embora desajeitado, o # notação é inválida fora de uma definição de classe. Tentativa de acesso myObject.#secret pode gerar um erro de sintaxe.

O debate continuará, mas, gostem ou não, os campos de classe foram adotados em vários mecanismos JavaScript. Eles estão aqui para ficar.

Fonte: https://www.sitepoint.com/javascript-private-class-fields/?utm_source=rss

local_img

Inteligência mais recente

local_img