Home >Web Front-end >JS Tutorial >JavaScript's New Private Class Fields, and How to Use Them

JavaScript's New Private Class Fields, and How to Use Them

Joseph Gordon-Levitt
Joseph Gordon-LevittOriginal
2025-02-10 14:30:15755browse

JavaScript's New Private Class Fields, and How to Use Them

ES6 introduces classes for JavaScript, but for complex applications, they can be too simple. Class fields (also known as class attribute ) are designed to simplify constructors with private and static members. The proposal is currently in TC39 Phase 3: Candidate Stage and will likely be added to ES2019 (ES10). Node.js 12, Chrome 74, and Babel currently support private fields. Before we understand how class fields are implemented, it is useful to review the ES6 class. This article was updated in 2020. For more in-depth JavaScript knowledge, read our book "JavaScript: From Newbie to Ninja, Second Edition".

Key Points

  • ES6 introduces classes for JavaScript, but for complex applications, they can be too simple. The class fields of ES2019 are designed to simplify the constructor with private and static members that may be added to ES2019 (ES10).
  • The new class field implementation allows the initialization of public properties outside of any constructor at the top of the class. If the constructor is still needed, the initializer is executed before it runs.
  • The class fields in ES2019 allow private class fields to be declared using a hash# prefix. This makes the field accessible only in the inner methods of the class, not from outside the class.
  • The use of # to represent privateness in class fields has been criticized, mainly because it is not as intuitive as private keywords. However, the # symbol is invalid outside the class definition, ensuring that the private fields remain private.
  • The immediate benefit of ES2019 class fields is the cleaner React code. They can be assigned as a function using the ES6 => fat arrow, which is automatically bound to the definition object without binding declarations.

ES6 category basics

Developers from languages ​​such as C, C#, Java, and PHP may be confused by the object-oriented inheritance model of JavaScript. Therefore, ES6 introduced the class . They are mostly syntactic sugar, but offer a more familiar concept of object-oriented programming. A class is an object template that defines how objects of that type behave. The following Animal class defines general animals (classes are usually represented in initial capital letters to distinguish them from objects and other types):

<code class="language-javascript">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`);
  }

}</code>

Class declarations are always executed in strict mode. No need to add 'use strict'. Constructor method runs when creating an object of type Animal. It usually sets initial properties and handles other initializations. speak() and walk() are instance methods that add more functionality. You can now use the new keyword to create objects from this class:

<code class="language-javascript">let rex = new Animal('Rex', 4, 'woof');
rex.speak();          // Rex says "woof"
rex.noise = 'growl';
rex.speak();          // Rex says "growl"</code>

Getter and Setter

Setter is a special method used only to define values. Similarly, Getter is a special method for return values ​​only. For example:

<code class="language-javascript">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.</code>

Subclass or subclass

It is usually possible to use one class as the base class for another. The Human class can inherit all properties and methods in the Animal class using the extends keyword. Properties and methods can be added, removed, or changed as needed to create human objects easier and clearer:

<code class="language-javascript">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`);
  }

}</code>

super refers to the parent class, so it is usually the first call called in the constructor. In this example, the Human speak() method overrides the method defined in Animal. Now you can create an instance of Human object:

<code class="language-javascript">let rex = new Animal('Rex', 4, 'woof');
rex.speak();          // Rex says "woof"
rex.noise = 'growl';
rex.speak();          // Rex says "growl"</code>

Static methods and properties

Defining methods using static keywords allows it to be called on a class without creating an object instance. Consider Math.PI constants: There is no need to create a Math object before accessing PI properties. ES6 does not support the same static properties as other languages, but properties can be added to the class definition itself. For example, the Human class can be adapted to keep counts of how many human objects have been created:

<code class="language-javascript">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.</code>
The static COUNT getter of the

class will return the number of humans accordingly:

<code class="language-javascript">class Human extends Animal {

  constructor(name) {

    // 调用Animal构造函数
    super(name, 2, 'nothing of interest');
    this.type = 'human';

  }

  // 重写Animal.speak
  speak(to) {

    super.speak();
    if (to) console.log(`to ${to}`);

  }

}</code>

ES2019 Class Fields (New)

New class field implementation allows the initialization of public properties outside of any constructor at the top of the class:

<code class="language-javascript">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.</code>

This is equivalent to:

<code class="language-javascript">class Human extends Animal {

  constructor(name) {

    // 调用Animal构造函数
    super(name, 2, 'nothing of interest');
    this.type = 'human';

    // 更新Human对象的计数
    Human.count++;

  }

  // 重写Animal.speak
  speak(to) {

    super.speak();
    if (to) console.log(`to ${to}`);

  }

  // 返回人类对象的个数
  static get COUNT() {
    return Human.count;
  }

}

// 类的静态属性本身 - 不是其对象
Human.count = 0;</code>

If you still need the constructor, the initializer will be executed before it runs.

Static class field

In the example above, static properties are added to the class definition object in an inelegant way after defining the class object. You don't need to use class fields:

<code class="language-javascript">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</code>

This is equivalent to:

<code class="language-javascript">class MyClass {

  a = 1;
  b = 2;
  c = 3;

}</code>

Private Class Field

All attributes in the

ES6 class are public by default and can be checked or modified outside the class. In the Animal example above, nothing can prevent changing the food property without calling the eats setter:

<code class="language-javascript">class MyClass {

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

}</code>
Other languages ​​usually allow declaration of private attributes. This is not possible in ES6, so developers often use underscore conventions (_propertyName), closures, symbols, or WeakMap to solve this problem. The underscore gives developers a hint, but nothing can prevent them from accessing the property. In ES2019, private class fields are defined using hash# prefix:

<code class="language-javascript">class MyClass {

  x = 1;
  y = 2;
  static z = 3;

}

console.log( MyClass.z ); // 3</code>
Please note that private methods, getters, or setters cannot be defined. TC39 Phase 3: Draft proposal recommends the use of a hash# prefix for names, which is implemented in Babel. For example:

<code class="language-javascript">class MyClass {

  constructor() {
    this.x = 1;
    this.y = 2;
  }

}

MyClass.z = 3;

console.log( MyClass.z ); // 3</code>

Benefit immediately: More concise React code!

React components usually have methods that are bound to DOM events. To ensure this resolves to a component, it is necessary to bind each method accordingly. For example:

<code class="language-javascript">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';      // 标准setter
rex.food = 'tofu';          // 完全绕过eats setter
console.log( rex.dinner );  // Rex eats tofu for dinner.</code>
When incCount is defined as an ES2019 class field, it can be assigned as a function using the ES6 => fat arrow, which is automatically bound to the definition object. No need to add binding statement:

<code class="language-javascript">class MyClass {

  a = 1;          // .a是公共的
  #b = 2;         // .#b是私有的
  static #c = 3;  // .#c是私有的和静态的

  incB() {
    this.#b++;
  }

}

let m = new MyClass();

m.incB(); // 运行正常
m.#b = 0; // 错误 - 私有属性不能在类外部修改</code>

Class field: Improved?

The definition of ES6 class is too simple. ES2019 class fields require less code, improve readability, and implement some interesting possibilities for object-oriented programming. Using # means privateness has received some criticism, mainly because it is ugly and feels like a skill. Most languages ​​implement the private keyword, so trying to use that member outside the class will be rejected by the compiler. JavaScript is interpreted. Consider the following code:

<code class="language-javascript">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`);
  }

}</code>

This will throw a runtime error on the last line, but it is just a serious consequence of trying to set the property. JavaScript is deliberately tolerant, and ES5 allows modifying properties on any object. Although clumsy, the # symbol is invalid outside the class definition. Trying to access myObject.#secret may throw a syntax error. This debate will continue, but class fields are already adopted in multiple JavaScript engines, like it or not. They will continue to exist.

FAQs (FAQs) about JavaScript Private Class Fields

What are the benefits of using JavaScript private class fields?

Private class fields in JavaScript provide a way to encapsulate or hide data in a class, which is the basic principle of object-oriented programming. This encapsulation ensures that the internal state of an object can only be changed by a class explicitly defined way. This makes the code more robust and predictable because it prevents external code from accidentally changing the state of the object in an unexpected way. Additionally, private fields can help simplify the interface of classes because they reduce the number of properties and methods exposed to the outside world.

How to declare private class fields in JavaScript?

In JavaScript, private class fields are declared by adding a hash (#) symbol before the field name. For example, to declare a private field named "value" in a class, you can write a #value. This field can then be accessed only in the inner methods of the class, not from outside the class.

Can I access private class fields from outside the class?

No, private class fields in JavaScript cannot be accessed from outside the class. This is by design, because one of the main uses of private fields is to hide internal data from the outside world. If you try to access a private field from outside the class, JavaScript will throw a syntax error.

Can I use private class fields with public fields?

Yes, JavaScript classes can have both private and public fields. Public fields are declared the same way as private fields, but have no hash (#) prefix. Unlike private fields that can only be accessed from inside a class, public fields can be accessed and modified from inside and outside the class.

How are private class fields different from private methods in JavaScript?

Private class fields and private methods in JavaScript have similar uses, both of which provide a way to hide internal details of a class from the outside world. However, they are used differently. Private fields are used to store data that can only be accessed inside the class, while private methods are functions that can only be called inside the class.

Can I use private class fields in all JavaScript environments?

Private class fields are relatively new features in JavaScript, so not all environments support it. At the time of writing, the latest versions of most major browsers, including Chrome, Firefox, and Safari, support private fields. However, Internet Explorer does not support them. If you need to write code that runs in all browsers, you may need to use a converter like Babel to convert the code into a form that older browsers can understand.

How to use private class fields in inner methods of a class?

To use a private class field in an internal method of a class, simply refer to the field with its name (prefixed with a hash (#) symbol). For example, if you have a private field called "value", you can access it in your method like this: this.#value.

Can I use private class fields in subclasses?

Yes, private class fields can be used in subclasses of JavaScript. However, each subclass has its own independent set of private fields that are not shared with superclasses or other subclasses. This means that if the private field declared by the subclass is of the same name as the private field in the superclass, the two fields are completely independent and do not interfere with each other.

Can I use private class fields in static methods?

No, private class fields in JavaScript cannot be used in static methods. This is because static methods are associated with the class itself, not with instances of the class, and private fields are accessible only in instances of the class.

Can I iterate over private class fields in JavaScript?

No, private class fields in JavaScript are not included in the object's property iteration. This is by design, because one of the main uses of private fields is to hide internal data from the outside world. If you need to iterate over the properties of the object, you should use a public field or method instead.

This response maintains the original image format and placement, and paraphrases the text while preserving the original meaning. The code examples remain largely unchanged as significant alteration would change the meaning.

The above is the detailed content of JavaScript's New Private Class Fields, and How to Use Them. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn