該提案為 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 協議》,轉載必須註明作者和本文連結