要定義在單擊時遞增的計數器窗口組件,可使用ES2015定義如下內容:程序員
class Counter extends HTMLElement {
clicked() {
this.x++;
window.requestAnimationFrame(this.render.bind(this));
}
constructor() {
super();
this.onclick = this.clicked.bind(this);
this.x = 0;
}
connectedCallback() { this.render(); }
render() {
this.textContent = this.x.toString();
}
}
window.customElements.define('num-counter', Counter);
複製代碼
使用ESnext字段聲明提議,上面的示例能夠寫成:編程
class Counter extends HTMLElement {
x = 0;
clicked() {
this.x++;
window.requestAnimationFrame(this.render.bind(this));
}
constructor() {
super();
this.onclick = this.clicked.bind(this);
}
connectedCallback() { this.render(); }
render() {
this.textContent = this.x.toString();
}
}
window.customElements.define('num-counter', Counter);
複製代碼
在上面的示例中,您能夠看到使用語法x = 0聲明的字段。您還能夠將沒有初始值設定項的字段聲明爲x。bash
下面的示例有一些實現細節,屬性能夠更好地保留在內部。 使用ESnext私有字段和方法,能夠將定義細化爲:閉包
class Counter extends HTMLElement {
#x = 0;
clicked() {
this.#x++;
window.requestAnimationFrame(this.render.bind(this));
}
constructor() {
super();
this.onclick = this.clicked.bind(this);
}
connectedCallback() { this.render(); }
render() {
this.textContent = this.#x.toString();
}
}
window.customElements.define('num-counter', Counter);
複製代碼
要使字段私有,只需給它們一個以#開頭的名稱。異步
經過定義在類外部不可見的內容,ESnext提供了更強大的封裝,確保您的類的用戶不會由於依賴於內部而產生意外。編程語言
請注意,ESnext僅在字段聲明中提供預先聲明的私有字段,正常屬性的方式不能建立。函數
公有屬性聲明使用Object.defineProperty(咱們在TC39術語中將[[Define]]語義引用)定義實例上的字段,而不是使用this.field = value; (稱爲[[Set]]語義)。 如下是影響的示例:工具
class A {
set x(value) { console.log(value); }
}
class B extends A {
x = 1;
}
複製代碼
使用所採用的語義,new B()將致使一個對象具備值爲1的屬性x,而且不會將任何內容寫入控制檯。 使用備用[[Set]]語義,1將被寫入控制檯,而且嘗試訪問該屬性將致使TypeError(由於缺乏getter)。優化
在[[Set]]和[[Define]]之間進行選擇是一種設計決策,它對比了不一樣類型的行爲預期:預期字段將做爲數據屬性建立,而無論超類包含什麼;預期setter將被調用。通過長時間的討論,TC39肯定了[[Define]]語義,發現保持第一個指望很重要。ui
基於Object.defineProperty的公共字段語義的決定是基於TC39內部的普遍討論和與開發人員社區的協商。不幸的是,社區至關分裂,而TC39則強烈支持Object.defineProperty。
做爲一種緩解,decorators建議提供了編寫decorator的工具,使公共字段聲明使用[[Set]]語義。即便您不一樣意默認值,也可使用其餘選項。(不管TC39選擇哪一種缺省值,都是這種狀況。)
公共字段在Chrome 72中帶有[[Define]]語義,這個語義決定不太可能被從新訪問。
不管是否存在初始化,公共字段聲明和私有字段聲明都會在實例中建立一個字段。若是沒有初始化器,則將字段設置爲undefined。這與某些轉置器實現稍有不一樣,後者將徹底忽略沒有初始化器的字段聲明。
例如,在下面的示例中,new D將生成一個對象,其y屬性未定義,而不是1。
class C {
y = 1;
}
class D extends C {
y;
}
複製代碼
將沒有初始化項的字段設置爲undefined(而不是擦除它們)的語義是,字段聲明提供了可靠的基礎,以確保在建立的對象上呈現屬性。這有助於程序員將對象保持在相同的通常狀態,這能夠很容易地進行推理,有時在實現中更易於優化。
私有字段基於使用#的語法,在聲明字段和訪問字段時都使用#。
class X {
#foo;
method() {
console.log(this.#foo)
}
}
複製代碼
這種語法試圖既簡潔又直觀,儘管它與其餘編程語言有很大的不一樣。 沒有私有的計算屬性名:#foo是一個私有標識符,#[foo]是一個語法錯誤。
私有字段提供了一個強大的封裝邊界:從類外部訪問私有字段是不可能的,除非有一些顯式代碼來公開它(例如,提供getter)。這與JavaScript屬性不一樣,JavaScript屬性支持各類反射和元編程,而相似於閉包和WeakMap等機制,這些機制不提供對其內部的訪問。
在構造函數運行時,公共字段和私有字段都按聲明的順序添加到實例中。初始化器將爲每一個類實例從新計算。字段在初始化器運行後當即添加到實例中,而後再計算下面的初始化器。
做用域:做爲初始化器表達式中的這個值,正在構造的實例位於做用域中。新的。目標未定義,如在方法中。對參數的引用是早期的錯誤。超方法調用Super .method()在初始化器中可用,可是超構造函數調用Super()是一個語法錯誤。即便類在異步函數/genenerator中聲明,初始化器中也不能使用wait和yield。
當計算字段初始化器並將字段添加到實例時:
若是派生類中沒有調用super(),而且沒有將其餘一些公共和私有字段添加到實例中,也沒有計算初始化器。對於基類,初始化器老是被求值,即便構造函數最終返回其餘東西。new.initialize proposal將添加一種方法,以編程方式向一個實例添加字段,而該實例不是來自基類中的super()/這個值。