JS 实例字段与公共字段 (ES2022):简化类属性声明

各位观众,大家好!今天咱们来聊聊 JavaScript ES2022 里一个挺有意思的特性:实例字段与公共字段,简单地说,就是关于类属性声明方式的革新。这玩意儿能让你的代码更简洁,可读性更高,还能减少一些潜在的 Bug。别担心,我会尽量用大白话,结合代码例子,帮你彻底搞懂它。

一、为啥要搞这个新玩意儿?之前的写法有啥问题?

在 ES2022 之前,咱们定义类属性,通常会在构造函数 constructor 里面,或者直接挂在 prototype 上。

class Dog {
  constructor(name, breed) {
    this.name = name;
    this.breed = breed;
  }

  bark() {
    console.log("Woof!");
  }
}

Dog.prototype.species = "Canis familiaris"; // 挂在原型上

这种写法有几个问题:

  1. 分散性: 属性定义和初始化散落在 constructorprototype 里,代码稍微一多,就容易找不着北,维护起来费劲。
  2. 不直观: 从类的定义里,很难一眼看出这个类有哪些实例属性。必须得仔细阅读 constructor 才能知道。
  3. 类型推断困难: TypeScript 这种静态类型语言,想准确推断类的属性类型,也得费点劲。

总而言之,之前的写法不够清晰明了,不利于代码的组织和维护。

二、ES2022 的解决方案:实例字段声明

ES2022 引入了实例字段声明,允许你在类体里直接声明类的属性,就像这样:

class Cat {
  name; // 实例字段声明
  age = 0; // 实例字段声明 + 初始化

  constructor(name) {
    this.name = name;
  }

  meow() {
    console.log("Meow!");
  }
}

const myCat = new Cat("Whiskers");
console.log(myCat.name); // 输出: Whiskers
console.log(myCat.age);  // 输出: 0

看到了吧? nameage 这两个属性,直接在类体里声明了。name 只是声明,没有初始化,所以默认值是 undefinedage 声明的同时还进行了初始化,所以默认值是 0

这种写法的好处显而易见:

  1. 集中性: 所有实例属性都集中在类体顶部声明,一目了然。
  2. 直观性: 类的结构更加清晰,一眼就能看出它有哪些属性。
  3. 类型推断友好: TypeScript 可以更轻松地推断属性类型。

三、公共字段、私有字段和静态字段

ES2022 不止引入了实例字段,还区分了公共字段、私有字段和静态字段。

  1. 公共字段 (Public Fields): 就是我们上面看到的,直接声明的字段,可以在类的外部访问。

  2. 私有字段 (Private Fields):# 开头的字段,只能在类的内部访问。

  3. 静态字段 (Static Fields): 使用 static 关键字声明的字段,属于类本身,而不是类的实例。

咱们分别来看例子:

3.1 公共字段

class Rectangle {
  width = 10;
  height = 5;

  getArea() {
    return this.width * this.height;
  }
}

const rect = new Rectangle();
console.log(rect.width);  // 输出: 10
rect.width = 20;           // 可以直接修改
console.log(rect.getArea()); // 输出: 100

3.2 私有字段

class Counter {
  #count = 0; // 私有字段

  increment() {
    this.#count++;
  }

  getCount() {
    return this.#count;
  }
}

const counter = new Counter();
counter.increment();
console.log(counter.getCount()); // 输出: 1
// console.log(counter.#count); // 错误!无法在类外部访问私有字段

注意,私有字段只能在类的内部通过 this.#count 访问,在类的外部访问会报错。这有助于实现数据的封装,防止意外修改。

3.3 静态字段

class Circle {
  static PI = 3.14159; // 静态字段

  constructor(radius) {
    this.radius = radius;
  }

  getArea() {
    return Circle.PI * this.radius * this.radius;
  }
}

console.log(Circle.PI); // 输出: 3.14159
const circle = new Circle(5);
console.log(circle.getArea()); // 输出: 78.53975

静态字段通过 类名.字段名 的方式访问,而不是通过类的实例。

四、初始化时机和顺序

了解了字段的类型,咱们再来看看它们的初始化时机和顺序。

  • 公共字段和私有字段:constructor 执行之前,按照声明的顺序初始化。
  • 静态字段: 在类定义时初始化。

看个例子:

class MyClass {
  field1 = this.calculateInitialValue(); // 使用 this
  static staticField = "Static Value";

  constructor() {
    console.log("Constructor called");
    console.log("field1:", this.field1);
  }

  calculateInitialValue() {
    console.log("Calculating initial value");
    return "Initial Value";
  }
}

const myInstance = new MyClass();
// 输出顺序:
// Calculating initial value
// Constructor called
// field1: Initial Value

console.log(MyClass.staticField); // 输出: Static Value

从输出结果可以看出:

  1. calculateInitialValueconstructor 之前调用,说明实例字段的初始化早于 constructor
  2. 静态字段在类定义时就初始化了。

五、一些使用技巧和注意事项

  1. 尽量初始化: 声明字段时,尽量同时进行初始化,可以避免出现 undefined

  2. 合理使用私有字段: 如果某个字段不希望被外部修改,就声明为私有字段。

  3. 避免在构造函数中重新赋值: 尽量在字段声明时初始化,避免在 constructor 中重复赋值,保持代码简洁。

  4. 考虑使用 TypeScript: 结合 TypeScript 的类型系统,可以更好地利用实例字段声明的优势,提高代码的健壮性。

六、与之前的写法对比:一个更复杂的例子

咱们用一个稍微复杂点的例子,对比一下 ES2022 的写法和之前的写法。

场景: 定义一个 Person 类,包含姓名、年龄和地址,并且有一个方法可以打印个人信息。

ES2022 写法:

class Person {
  name;
  age = 0;
  #address = "Unknown"; // 私有字段

  constructor(name, age, address) {
    this.name = name;
    this.age = age;
    this.#address = address; // 初始化私有字段
  }

  get address() {
    return this.#address;
  }

  set address(newAddress) {
    this.#address = newAddress;
  }

  printInfo() {
    console.log(`Name: ${this.name}, Age: ${this.age}, Address: ${this.#address}`);
  }
}

const person = new Person("Alice", 30, "123 Main St");
person.printInfo(); // 输出: Name: Alice, Age: 30, Address: 123 Main St
console.log(person.address); // 输出: 123 Main St
person.address = "456 Oak Ave";
person.printInfo(); // 输出: Name: Alice, Age: 30, Address: 456 Oak Ave

之前的写法:

class Person {
  constructor(name, age, address) {
    this.name = name;
    this.age = age;
    this._address = address; // 使用 _ 前缀表示私有属性
  }

  get address() {
    return this._address;
  }

  set address(newAddress) {
    this._address = newAddress;
  }

  printInfo() {
    console.log(`Name: ${this.name}, Age: ${this.age}, Address: ${this._address}`);
  }
}

Person.prototype.age = 0; // 在原型上定义默认值
const person = new Person("Alice", 30, "123 Main St");
person.printInfo();
console.log(person.address);
person.address = "456 Oak Ave";
person.printInfo();

对比一下,可以发现:

  1. ES2022 的写法更加简洁明了,类的结构一目了然。
  2. ES2022 使用 # 实现了真正的私有字段,而之前的写法只是约定俗成地使用 _ 前缀,并不能阻止外部访问。
  3. ES2022 的写法避免了在 prototype 上定义属性,更加集中。

七、兼容性

ES2022 的这个特性,需要较新的 JavaScript 引擎支持。 如果你的项目需要兼容旧版本的浏览器或 Node.js,可能需要使用 Babel 等工具进行转译。

八、总结

ES2022 的实例字段声明,是 JavaScript 在类定义方面的一次重要改进。它让代码更简洁、更易读、更易维护,并且提供了真正的私有字段支持。 虽然需要注意兼容性问题,但总的来说,这是一个值得掌握的新特性。 掌握它,能让你的 JavaScript 代码更加现代化,更加优雅。

希望今天的讲座能让你对 JavaScript ES2022 的实例字段与公共字段有一个清晰的理解。 记住,实践是检验真理的唯一标准,多写代码,多尝试,才能真正掌握它! 祝大家编程愉快!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注