Что такое «поля классов» в JavaScript?

#javascript

Вопрос:

Я читал о классах JavaScript и наткнулся на этот термин «синтаксис полей общедоступных классов«. Копнув немного глубже, я наткнулся на документацию этого Вавилона по свойствам классов.

Может ли кто — нибудь объяснить, пожалуйста, с точки зрения реализации, каковы варианты использования этого нового синтаксиса? (Какие решения/преимущества он предлагает для JavaScript, которых до сих пор не хватало?)

Вот пример ниже (запускался без ошибок в Google Chrome):

 class Person {
    firstName = "Mike";
    lastName = "Patel";
    
    // this is a public class field syntax
    getName = () => {
      return this.firstName   " "   this.lastName;
    };
}

var p = new Person();

console.log(p.firstName); // Mike
console.log(p.lastName); // Patel
console.log(p.getName); // () => { return this.firstName   " "   this.lastName; }
console.log(typeof p.getName); // function
console.log(p.getName()); // Mike Patel 

Комментарии:

1. Что значит «особенный»? Если вы ожидаете, что это сделает что — то странное и неинтуитивное — тогда нет, это делает именно то, что вы, вероятно, думаете, — это создает два свойства. Это просто новый синтаксис, который сначала был недоступен. В Firefox 68 это выбрасывает SyntaxError: fields are not currently supported

2. @VLAZ Я проверил в Google Chrome, и он был выполнен без каких-либо ошибок.

3. Это может быть полезно: medium.com/@jacobworrel/…

4. @AadityaSharma опять же, это в Firefox. Это более новый синтаксис, поэтому он не везде реализован полностью. В этом-то и был смысл. Это единственное, что в нем «особенного», но я не знаю, считаете ли вы это «особенным» или нет.

5. @AadityaSharma итак, вместо «что особенного» вы ищете «каков вариант использования». Потому что сам синтаксис не является особенным. Вариант использования заключается в том, почему вы выбрали бы этот, а не другой код.

Ответ №1:

Проще говоря, причина использования этого-простота понимания кода. Без объявлений полей классов вы могли бы сделать что-то вроде:

 class Person {
  constructor() {
    this.firstName = "Mike";
    this.lastName = "Patel";

    this.getName = () => {
      return this.firstName   " "   this.lastName;
    };
  }
}

var p = new Person();

console.log(p.firstName); // Mike
console.log(p.lastName); // Patel
console.log(p.getName); // () => { return this.firstName   " "   this.lastName; }
console.log(typeof p.getName); // function
console.log(p.getName()); // Mike Patel 

Это работает, но теперь у вас есть как вызываемые getName() , так и остальные свойства простого экземпляра, собранные в конструкторе. У вас может быть еще больше, а это означает, что определение вашего класса в целом будет выглядеть довольно бессмысленным:

 class MyClass() {
  constructor(someArg) {
    this.foo1 = 1;
    this.foo2 = 2;
    this.foo3 = 3;
    this.foo4 = someArg;

    this.bar1 = () => {}
    this.bar2 = () => {}
    this.bar3 = () => {}
    this.bar4 = () => {}
  }
}
 

и так далее. Опять же, все находится в конструкторе. Если у вас много кода, становится все труднее понять, что есть что. И если конструктор принимает какие-либо аргументы, то у вас есть дополнительные накладные расходы на их отслеживание. Поэтому его трудно читать, трудно поддерживать, и все это без какой-либо реальной пользы. Вы запихиваете все в одно и то же место.

С объявлениями полей классов вы разделяете их и получаете

 class MyClass() {
  /* properties - do not depend on the constructor*/
  foo1 = 1;
  foo2 = 2;
  foo3 = 3;
  foo4; /* this is a property that this class will have - 
          I do not need to look at the constructor to know about it */

  /* easy to see what the constructor does that is only about *constructing* the object */
  constructor(someArg) {
    this.foo4= someArg;
  }

  /* callable field are separated from the rest of the simple properties and construction logic */
  bar1 = () => {}
  bar2 = () => {}
  bar3 = () => {}
  bar4 = () => {}
}
 

Таким образом, в целом, это не революционно, но это немного более приятный синтаксис, который облегчает выражение того, что есть у класса.

Комментарии:

1. Поздно для игры в JS с одним вопросом на эту тему, поймите, что предложение поля класса направлено на упрощение метода конструктора. Это то, что нам рекомендуется использовать?

2. @TommyLeong Я бы сказал, что да. Я не вижу реальной пользы в объявлении всего в конструкторе. Эти два метода либо равны, либо поля класса превосходят друг друга.

3. Таким образом, не имеет значения, сможем ли мы использовать это/(другое) предложение, если оно пройдет стадию 4, мы сможем его использовать? Нет никаких проблем в их использовании? Тоже новичок в TC39.

4. @TommyLeong если вы уверены, что среда, в которой вы запускаете свой код, поддерживает поля классов или, по крайней мере, вы используете Babel, то проблем быть не должно.

5. @Bergi очень признателен. Я действительно был небрежен с терминологией, которую использовал. Я перефразировал, чтобы избежать путаницы.

Ответ №2:

Чтобы процитировать предложение по полям классов

Объявляя поля заранее, определения классов становятся более самодокументированными; экземпляры проходят через меньшее количество переходов состояний, так как объявленные поля всегда присутствуют.

Введение полей классов также позволяет создавать частные поля классов, которые также имеют несколько преимуществ:

Определяя вещи, которые не видны за пределами класса, ESnext обеспечивает более надежную инкапсуляцию, гарантируя, что пользователи ваших классов случайно не запутаются в зависимости от внутренних компонентов, которые могут меняться от версии к версии.

Ответ №3:

Оно исходит из этого предложения, в котором рассматривается «проблема».

ПРЕДВАРИТЕЛЬНОЕ ПРЕДЛОЖЕНИЕ

Предположим, что вы хотите иметь класс Foo , содержащий атрибут по умолчанию, затем вы можете написать по умолчанию следующее

 class Foo {
  defaultAttribute = 'default';
  getDefault() { return this.defaultAttribute; }
}
 

Помните, что приведенное выше предложение не было реализовано банкоматом. Это defaultAttribute = '...' игнорируется при настройке объекта класса (при компиляции). Он даже не является частью прототипов или элементов поля (объекта функции).
Это происходит потому defaultAttribute , что компилятор не улавливает. Поэтому вы не можете этого сделать foo.defaultAttribute .

Вызов getDefault() вызовет здесь ошибку, потому что в этот момент она не определена. Эта функция работает, если вы указываете значение defaultAttribute в конструкторе;

 class Foo {
  defaultAttribute = 'default';
  constructor() {
    this.defaultAttribute = 'hello';
  }
  getDefault() { return this.defaultAttribute; }
}
 

В этой ситуации значение defaultAttribute имеет значение «Привет», но оно не переопределило исходную переменную с 'default' помощью .

ПОСЛЕ ПРЕДЛОЖЕНИЯ

С помощью этого предложения проблема «игнорирования» решается так, что вы можете сделать то, что вы только что описали:

 class Foo {
  defaultAttribute = 'default';
  getDefault() { return this.defaultAttribute; }
}
 

С помощью этого вы можете пропустить использование constructor()

 class Foo1 {
  defaultAttribute = 'default';
  getDefault() { return this.defaultAttribute; }
}

class Foo2 {
  defaultAttribute = 'default';
  constructor() {this.defaultAttribute = 'hello';}
  getDefault() { return this.defaultAttribute; }
}

const foo1 = new Foo1();
const foo2 = new Foo2();

console.log(foo1.getDefault());
console.log(foo2.getDefault()); 

Комментарии:

1. » defaultAttribute = '...' Игнорируется при настройке объекта класса (при компиляции) » — нет, это не так. Перед реализацией предложения интуитивно напишите, что просто вызовет синтаксическую ошибку. » Вызов getDefault() приведет к ошибке здесь, потому что в этот момент она не определена». — нет. Доступ к несуществующим свойствам в JS просто возвращает undefined , ошибка не возникает.