JavaScript中的"class fields"是什么?

What are "class fields" in JavaScript?

我正在阅读有关 JavaScript classes 的内容,并遇到了这个术语“public class 字段语法 ” .在深入研究时,我发现了这个 Babel's documentation on class properties

有人可以解释一下 - 这个新语法的实现方面有哪些用例? (它提供了什么 solutions/benefits 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

引用自class fields proposal

By declaring fields up-front, class definitions become more self-documenting; instances go through fewer state transitions, as declared fields are always present.

class 字段的引入还允许 私有 class 字段,这也带来了一些好处:

By defining things which are not visible outside of the class, ESnext provides stronger encapsulation, ensuring that your classes' users don't accidentally trip themselves up by depending on internals, which may change version to version.

简单来说,使用这个的原因是代码易于理解。如果没有 class 字段声明,您将执行如下操作:

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 定义总体上看起来毫无意义:

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 字段声明,将它们分开并得到

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 = () => {}
}

所以,总而言之,它不是革命性的,但它的语法稍微好一点,可以更容易地表达 class 的内容。

它起源于 this proposal,其中 "issue" 正在被解决。

预提案

假设你想要一个拥有默认属性的class Foo,那么你可以直接写成下面的

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

请记住,上面的提案没有在 atm 上实施。 defaultAttribute = '...' 在设置 class 对象(编译时)时被忽略。它甚至不是原型或字段成员(函数对象的)的一部分。 那是因为 defaultAttribute 没有被编译器拾取。因此你不能做foo.defaultAttribute

调用getDefault()会在此处抛出错误,因为此时它是未定义的。如果您在构造函数中为 defaultAttribute 提供值,该函数确实有效;

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

在这种情况下,defaultAttribute 设置为 'Hello' 但它 而不是 'default' 覆盖原始变量。

POST-提案

通过该提案,"ignoring" 问题得到解决,您可以按照您刚才描述的方式进行操作:

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());