几个提案扩展了现有的 JavaScript 类语法,增加了新的功能。本文将解释 V8 v7.2 和 Chrome 72 中新的公共类字段语法,以及即将推出的私有类字段语法。
以下是一个代码示例,它创建了一个名为 IncreasingCounter
的类的实例
const counter = new IncreasingCounter();
counter.value;
// logs 'Getting the current value!'
// → 0
counter.increment();
counter.value;
// logs 'Getting the current value!'
// → 1
请注意,访问 value
会在返回结果之前执行一些代码(即,它会记录一条消息)。现在问问自己,你将如何在 JavaScript 中实现这个类?🤔
ES2015 类语法 #
以下是使用 ES2015 类语法实现 IncreasingCounter
的方法
class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}
该类在原型上安装了 value
获取器和 increment
方法。更有趣的是,该类有一个构造函数,它创建了一个实例属性 _count
并将其默认值设置为 0
。我们目前倾向于使用下划线前缀来表示 _count
不应该被类使用者直接使用,但这只是一个约定;它并不是由语言强制执行的具有特殊语义的“私有”属性。
const counter = new IncreasingCounter();
counter.value;
// logs 'Getting the current value!'
// → 0
// Nothing stops people from reading or messing with the
// `_count` instance property. 😢
counter._count;
// → 0
counter._count = 42;
counter.value;
// logs 'Getting the current value!'
// → 42
公共类字段 #
新的公共类字段语法允许我们简化类定义
class IncreasingCounter {
_count = 0;
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}
_count
属性现在很好地声明在类的顶部。我们不再需要构造函数来定义一些字段。很不错!
但是,_count
字段仍然是一个公共属性。在这个特定的例子中,我们希望阻止人们直接访问该属性。
私有类字段 #
这就是私有类字段的作用。新的私有字段语法类似于公共字段,除了 你使用 #
将字段标记为私有。你可以将 #
视为字段名称的一部分
class IncreasingCounter {
#count = 0;
get value() {
console.log('Getting the current value!');
return this.#count;
}
increment() {
this.#count++;
}
}
私有字段在类主体之外是不可访问的
const counter = new IncreasingCounter();
counter.#count;
// → SyntaxError
counter.#count = 42;
// → SyntaxError
公共和私有静态属性 #
类字段语法也可以用来创建公共和私有静态属性和方法
class FakeMath {
// `PI` is a static public property.
static PI = 22 / 7; // Close enough.
// `#totallyRandomNumber` is a static private property.
static #totallyRandomNumber = 4;
// `#computeRandomNumber` is a static private method.
static #computeRandomNumber() {
return FakeMath.#totallyRandomNumber;
}
// `random` is a static public method (ES2015 syntax)
// that consumes `#computeRandomNumber`.
static random() {
console.log('I heard you like random numbers…');
return FakeMath.#computeRandomNumber();
}
}
FakeMath.PI;
// → 3.142857142857143
FakeMath.random();
// logs 'I heard you like random numbers…'
// → 4
FakeMath.#totallyRandomNumber;
// → SyntaxError
FakeMath.#computeRandomNumber();
// → SyntaxError
更简单的子类化 #
当处理引入额外字段的子类时,类字段语法的优势变得更加明显。想象一下以下基类 Animal
class Animal {
constructor(name) {
this.name = name;
}
}
要创建一个引入额外实例属性的 Cat
子类,你以前必须调用 super()
来运行 Animal
基类的构造函数,然后再创建该属性
class Cat extends Animal {
constructor(name) {
super(name);
this.likesBaths = false;
}
meow() {
console.log('Meow!');
}
}
这对于仅仅表示猫不喜欢洗澡来说,实在是太冗长了。幸运的是,类字段语法消除了对整个构造函数的需求,包括尴尬的 super()
调用
class Cat extends Animal {
likesBaths = false;
meow() {
console.log('Meow!');
}
}
功能支持 #
对公共类字段的支持 #
对私有类字段的支持 #
对私有方法和访问器的支持 #
- Chrome: 自版本 84 起支持
- Firefox: 自版本 90 起支持
- Safari: 支持
- Node.js: 自版本 14.6.0 起支持
- Babel: 支持