Aguarde...

12 de setembro de 2021

Blocos de inicialização estáticos de classe em JavaScript

Blocos de inicialização estáticos de classe em JavaScript

Os blocos de inicialização estáticos da classe são um recurso que fará parte da proposta ECMAScript 2022. Este é um dos recursos que você pode não usar com frequência. No entanto, ainda pode ser útil de vez em quando. Este tutorial o ajudará a aprender sobre o que são blocos de inicialização estática de classe e como usá-los.

Classes, campos e inicialização de campo em um breve

Ao criar uma classe JavaScript, você pode adicionar métodos e campos públicos, estáticos e privados. O tipo de propriedade ou método que você escolher dependerá da situação atual e de sua intenção. Os campos e métodos públicos são melhores para algumas situações, enquanto os privados são melhores para outras. Você pode inicializar esses campos ou não.

Você também pode definir e inicializar as propriedades da classe dentro do constructormétodo. Isso se torna especialmente útil quando você deseja criar propriedades de classe com base em parâmetros de classe. O constructormétodo permite que você inicialize também campos públicos e privados.

// Create a class:
class Person {
  // Define public field:
  isAlive = true
  numberOfLegs = 2
  numberOfArms = 2

  // Define private field:
  #iq

  // Define custom properties in constructor:
  constructor(name, age, iq) {
    this.name = name
    this.age = age

    // Initialize private field "iq":
    this.#iq = iq
  }

  // Add private method:
  #decreaseIq() {
    this.#iq--
  }

  // Add public methods:
  sayHello() {
    return `Hello, my name is ${this.name}.`
  }

  watchSitcom() {
    // Call private method "decreaseIq()":
    return this.#decreaseIq()
  }

  tellYourIq() {
    // Return value of private field "iq":
    return this.#iq
  }
}

// Create instance of Person class:
const josh = new Person('Josh', 31, 125)

// Log "josh":
console.log(josh)
// Output:
// Person {
//   isAlive: true,
//   numberOfLegs: 2,
//   numberOfArms: 2,
//   name: 'Josh',
//   age: 31,
//   __proto__: {
//     constructor: ƒ Person(),
//     sayHello: ƒ sayHello(),
//     watchSitcom: ƒ watchSitcom(),
//     tellYourIq: ƒ tellYourIq()
//   }
// }

// Call the "sayHello()" method:
josh.sayHello()
// Output:
// 'Hello, my name is Josh.'

// Watch some tv show:
josh.watchSitcom()

// Tell me your IQ:
josh.tellYourIq()
// Output:
// 124

O problema com campos estáticos (dica: inicialização)

Até agora, tudo parece bem. Aí vem o problema. O constructormétodo não permitirá que você inicialize campos estáticos. Isso pode não ser um problema real se todos os campos estáticos de que você precisa podem ser inicializados quando você os define. Você pode fazer isso da maneira usual. Você cria um novo campo de classe estática e atribui a ele algum valor.

// Create class:
class Library {
  // Add and initialize static field for books:
  static books = [
    { title: 'Lean UX', read: true },
    { title: 'Lean Customer Development', read: false },
    { title: 'The Four Steps to the Epiphany', read: false },
    { title: 'Lean Analytics', read: false }
  ]

  // Add second static field:
  static booksToReadCount = 3
}

// Log value of "booksToReadCount" field:
console.log(Library.booksToReadCount)
// Output:
// 3

A questão é: e se você quiser inicializar o campo estático de forma mais dinâmica? Veja a Libraryaula, por exemplo. Neste momento, requer atualização manual de ambos os campos booksbooksToReadCount, para mantê-los sincronizados. Isso pode funcionar de vez em quando, mas pode rapidamente se tornar uma tarefa irritante.

Pode-se pensar que isso pode ser resolvido com o constructormétodo. Você define um campo estático sem inicializá-lo ou inicializa-o com algum valor de espaço reservado. Em seguida, você adiciona constructore usa para atualizar o valor desse campo estático. O problema é que isso não funciona. Esse campo permanecerá undefinedou manterá o valor do espaço reservado.

class Library {
  // Add and initialize static field for books:
  static books = [
    { title: 'Lean UX', read: true },
    { title: 'Lean Customer Development', read: false },
    { title: 'The Four Steps to the Epiphany', read: false },
    { title: 'Lean Analytics', read: false }
  ]

  // Add static field, but don't initialize it:
  static booksToReadCount

  // Try to initialize static
  // "booksToReadCount" in constructor:
  constructor() {
    this.booksToReadCount = 3
  }
}

// Try to log the value of "booksToReadCount" field:
console.log(Library.booksToReadCount)
// Output:
// undefined

Solução com recursos externos

Uma maneira de resolver esse problema é usar recursos externos. Você pode criar uma nova função fora da classe que fará o que você precisa. Em seguida, você pode atribuir uma chamada a essa função para o campo estático. O valor retornado pela função se tornará o valor do campo estático.

// Create class:
class Library {
  // Add and initialize static field for books:
  static books = [
    { title: 'Lean UX', read: true },
    { title: 'Lean Customer Development', read: false },
    { title: 'The Four Steps to the Epiphany', read: false },
    { title: 'Lean Analytics', read: false }
  ]

  // Add second static field
  // and assign it the returned value of
  // "getBooksToReadCount()" function:
  static booksToReadCount = getBooksToReadCount(Library.books)
}

// Create function to get booksToRead count:
function getBooksToReadCount(books) {
  return books.filter(book => !book.read).length
}

// Log value of "version" field:
console.log(Library.booksToReadCount)
// Output:
// 3

Esta solução fará o trabalho. A desvantagem é que requer essa função externa. Se você usar essa abordagem várias vezes, seu código pode rapidamente se tornar menos claro.

Solução com blocos de inicialização estáticos

Existe uma solução alternativa. Esta solução são blocos de inicialização estática que vêm no ES2022. Esses blocos de inicialização estáticos permitem que você crie blocos de código dentro da classe. Você pode usar esses blocos para executar qualquer operação necessária. Vamos tomar a Libraryaula como exemplo novamente.

Você define a classe e define o primeiro campo estático bookse atribui a ele a matriz de livros. Em seguida, você define o segundo campo booksToReadCount, mas não o inicializa. Depois disso, você adiciona o bloco de inicialização estática. Dentro deste bloco, você executa qualquer operação necessária e inicializa o booksToReadCount.

class Library {
  // Add and initialize static field for books:
  static books = [
    { title: 'Lean UX', read: true },
    { title: 'Lean Customer Development', read: false },
    { title: 'The Four Steps to the Epiphany', read: false },
    { title: 'Lean Analytics', read: false }
  ]

  // Define static field for count,
  // but don't initialize it:
  static booksToReadCount;

  // Add static initialization block:
  static {
    // Initialize the "booksToReadCount" field:
    this.booksToReadCount = this.books.filter(book => !book.read).length
  }
}

// Log value of "version" field:
console.log(Library.booksToReadCount)
// Output:
// 3

Sintaxe e regras para blocos de inicialização estática

Algumas coisas importantes sobre blocos de inicialização estática que você deve saber. Primeiro, a sintaxe. A sintaxe é muito simples. Existe a staticpalavra – chave e o bloco de código definidos com chaves ( {}). O código que você deseja executar, incluindo a inicialização de campos estáticos, fica dentro do bloco de código.

// Create a class:
class MyClass {
  // Add some static field:
  static foo

  // Create static initialization block:
  static {
    // Initialize "foo"
    foo = 'Fizz'

    // And do something more...
  }
}

Uma coisa interessante a mencionar. Cada bloco de código também cria um novo escopo de bloco. Portanto, você também pode usar esses blocos para criar variáveis ​​temporárias de que possa precisar. Essas variáveis ​​existirão apenas dentro do bloco. Observe que isso se aplica apenas a variáveis ​​com escopo de bloco const e let . Não se aplica a var.

// Create a class:
class MyClass {
  // Create static initialization block:
  static {
    // Create some temporary variable:
    const randomNumber = 19

    // And do something...
  }
}

A segunda coisa é que você pode ter quantos blocos de inicialização estáticos em uma classe forem necessários.

// Create a class:
class Dragon {
  // Add static field:
  static trunkVolume

  // Create static initialization block:
  static {
    // Initialize "trunkVolume" field:
    this.trunkVolume = 6_000
  }

  // Add another static field:
  static diameter

  // Create static initialization block:
  static {
    // Initialize "diameter" field:
    this.diameter = 4
  }

  // Add another static field:
  static thrust

  // Create static initialization block:
  static {
    // Initialize "thrust" field:
    this.thrust = 400
  }
}

Terceiro, os blocos estáticos são executados durante a execução de inicializadores de campos estáticos. Quarto e último, se você estender classes, os campos estáticos de uma superclasse serão executados antes dos campos estáticos de suas subclasses.

// Create superclass:
class Vehicle {
  static {
    console.log('Vehicle initialization block.')
  }
}

// Create first subclass:
class Car extends Vehicle {
  static {
    console.log('Car initialization block.')
  }
}

// Create second subclass:
class Plane extends Vehicle {
  static {
    console.log('Plane initialization block.')
  }
}

// Output:
// 'Vehicle initialization block.'
// 'Car initialization block.'
// 'Plane initialization block.'

Conclusão: blocos de inicialização estática de classe em JavaScript

Se você costuma trabalhar com campos estáticos, pode achar que os blocos de inicialização estáticos são um recurso útil. Eles podem ajudar a tornar a inicialização estática mais fácil. Espero que este post tenha ajudado você a entender o que são blocos de inicialização estática de classe em JavaScript e como usá-los. Se você quiser saber mais sobre esse recurso, dê uma olhada na proposta disponível no GitHub .

Postado em Blog
Escreva um comentário