tc39 proposal: Class field declarations
该提案为 ES6 class 引入了类成员声明(Class field declarations)语法,包括公开成员和私有成员。目前处于 Stage 3 阶段。
下面举一个例子:
// 例子 1
class Foo {
publicVar = 1
#privateVar = 2
getPrivateVar() {
return this.#privateVar
}
}
const foo = new Foo()
foo.publicVar
foo.getPrivateVar()
公开成员在 class 中,直接以类似赋值语句的形式声明;私有成员的声明需要带上 #
,访问的时候也要带上它。
可以把上面的代码简单理解成下面这种书写方式:
// 例子 2
class Foo {
constructor() {
this.publicVar = 1
this._privateVar = 2
}
getPrivateVar() {
return this._privateVar
}
}
const foo = new Foo()
foo.publicVar
foo.getPrivateVar()
这种写法近似正确语义,但不精确。比如:
- public fields(也就是这里的
publicVar
)底层其实是使用Object.defineProperty
的方式([[Define]] 语义)定义的,而不是this.field = value
的方式([[Set]] 语义)定义的。 - private fields 要能真正对外隐藏成员变量,而上面的实现通过
foo._privateVar
依旧能拿到。
当然,我们还可以更近一步,使用 babel 转码“例子 1 ”的代码:
"use strict";
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _classPrivateFieldGet(receiver, privateMap) {
var descriptor = privateMap.get(receiver);
if (!descriptor) {
throw new TypeError("attempted to get private field on non-instance");
}
if (descriptor.get) {
return descriptor.get.call(receiver);
}
return descriptor.value;
}
var _privateVar = new WeakMap();
class Foo {
constructor() {
_defineProperty(this, "publicVar", 0);
_privateVar.set(this, {
writable: true,
value: 0
});
}
getPrivateVar() {
return _classPrivateFieldGet(this, _privateVar);
}
}
const foo = new Foo();
foo.publicVar;
foo.getPrivateVar();
babel 的转码更精确一些,定义公共成员使用了 _defineProperty
方法,而私有成员则使用 WeakMap 变量贮藏。
不过要再深究一点的话:
- 对于定义公共成员的
_defineProperty
方法,它并不是完全使用的 [[Define]] 语义,与提案中描述的不一致。
// √ 下列代码经 babel 转义后,语义不变
class A {
set x(value) { console.log(value); }
}
class B extends A {
x = 1;
}
// × 下列代码经 babel 转义后,采用 [[Set]] 语义,与提案中描述不符
class B {
x = 1;
}
- 而私有成员的 WeakMap 实现,依旧是通过
_privateVar
的形式实现,不过值通过 WeakMap 被间接包装了一下。
(完)
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: