tc39 proposal: Class field declarations

zhangbao發表於2020-09-28

提案地址:github.com/tc39/proposal-class-fields

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

相關文章