JavaScript 的新特性:類的 #private 欄位

來源:互聯網
上載者:User

標籤:oal   委員會   open   機制   商業   content   沒有   私人屬性   markdown   

這是什麼,如何使用,為什麼需要?

一邊聽“Noise Pollution” —— Portugal. The Man,一邊閱讀本文簡直就是享受

JavaScript 標準的第二階段(Stage 2)加入了類私人欄位。它還沒有最終確定,但 JavaScript 標準委員會認為這個特性會被開發出來並最終納入標準(雖然它可能還會改變)

它的文法(當前)看起來像這樣:

 1 class Point { 2   **#x;** 3   **#y;** 4  5   constructor(x, y) { 6     **this.#x** = x; 7     **this.#y** = y; 8   } 9 10   equals(point) {11     return **this.#x** === **point.#x** && **this.#y** === **point.#y**;12   }13 }

 

  

這個文法有兩個主要部分:

  • 定義私人欄位

  • 引用私人欄位

定義私人欄位

定義私人欄位與定義公用欄位基本相同:

1 class Foo {2   **publicFieldName =** 1;3   **#privateFieldName =** 2;4 }

 

為了訪問私人欄位,你需要定義它。如果你不想在定義的時候給它賦值,可以這樣:

1 class Foo {2   **#privateFieldName;**3 }

 

引用私人欄位

引用私人欄位與訪問其它屬性類似,只是它有一點特殊的文法。

1 class Foo {2   publicFieldName = 1;3   #privateFieldName = 2;4 5   add() {6     return this.publicFieldName + **this.#privateFieldName**;7   }8 }

 

this.# 也可以簡寫:

1 method() {2   **#privateFieldName;**3 }

 

它與下面這段代碼等價:

1 method() {2   **this.#privateFieldName;**3 }

 

引用執行個體(對象)的私人欄位

引用私人欄位並不局限於 this。也可以訪問類執行個體(對象)的私人欄位:

1 class Foo {2   #privateValue = 42;3 4   static getPrivateValue(**foo**) {5     return **foo.#privateValue**;6   }7 }8 9 Foo.getPrivateValue(**new Foo()**); // >> 42

 

這裡,foo 是 Foo 的執行個體,所以我們允許在類定義中去尋找 #privateValue。

私人方法(很快有會有?)

私人欄位這個提案只關注了為類添加私人欄位這個問題。提案並沒有提到會對類的方法做什麼改變,所以私人方法可能會在後續建議中提到,像這樣:

1 class Foo {2   constructor() {3     **this.#method();**4   }5 6   **#method() {**7     // ...8   **}**9 }

 

這之前,可以將函數賦值給私人欄位:

1 class Foo {2   constructor() {3     **this.#method();**4   }5 6   **#method = () => {**7     // ...8   **};**9 }

 

封裝

使用某個類的執行個體的時候,不能引用這個類的私人欄位。只能在定義這些私人欄位的類中引用它們。

 1 class Foo { 2   **#bar;** 3  4   method() { 5     **this.#bar;** // Works 6   } 7 } 8  9 let foo = new Foo();10 11 **foo.#bar;** // Invalid!

 

進一步說,作為真正的私人屬性,你應該不能檢測到私人欄位的存在。

為了確保你不能探測到私人欄位,我們需要允許擁有同樣名稱的公用欄位。

1 class Foo {2   bar = 1; // public bar3   #bar = 2; // private bar4 }

 

如果私人欄位不允許公用欄位有同樣的名稱,你就可以通過嘗試寫同名屬性來探測到私人欄位的存在:

1 foo.bar = 1; // Error: bar is private! (boom... detected)

 

或者不報錯的版本:

1 foo.bar = 1;2 console.log(foo.bar); // undefined (boom... detected again)

 

這種封裝同樣對子類有效。子類應該可以擁有相同名稱的私人欄位,而不必擔心父類是否已經存某個私人欄位是這個名稱。

1 class Foo {2   **#fieldName** = 1;3 }4 5 class Bar extends Foo {6   **fieldName** = 2; // Works!7 }

 

注意:關於封裝或“強私人”背後的動機,閱讀 FAQ 中這個部分。

那麼, 為什麼用 # 號[譯者註:URL 的 Hash標記]?

很多人想知道“為什麼不像其它語言那樣使用 private 關鍵字”?

如果使用這樣的文法,這裡有個樣本:

1 class Foo {2   **private value;**3 4   equals(foo) {5     return **this.value** === **foo.value**;6   }7 }

 

讓我們單獨看看文法的兩個部分。

為什麼不使用 private 關鍵字來聲明?

private 關鍵字在很多不同的語言中用於聲明私人欄位。

來看看使用這種文法的語言:

1 class EnterpriseFoo {2   public bar;3   private baz;4 5   method() {6     this.bar;7     this.baz;8   }9 }

 

在這些語言中,以同樣的方式訪問私人欄位和公用欄位。所以它們才會這樣定義。

但是在 JavaScript 中,我們不能使用 this.field 來引用私人屬性(稍後深入),我們需要一種基於文法的方法來串連它們的關係。這兩個地方使用 # 更能清楚的表明引用的是什麼。

什麼引用需要 # (Hash 標記)?

因為如下原因,我們需要使用 this.#field 而不是 this.field:

  1. 因為 #封裝 (參閱上面“封裝”一節),我們要能同時訪問公用和私人的同名欄位。因此訪問一個私人欄位不是按常規的方式尋找。

  2. JavaScript 中的公用欄位可以通過 this.field 或 this[‘field‘] 來訪問。私人欄位不能支援第二種文法(因為它需要是靜態),這樣會導致混淆。

  3. 你需要費勁去檢查:

來看一段範例程式碼。

 1 class Point { 2   **#x;** 3   **#y;** 4  5   constructor(x, y) { 6     **this.#x** = x; 7     **this.#y** = y; 8   } 9 10   equals(other) {11     return **this.#x** === **other.#x** && **this.#y** === **other.#y**;12   }13 }

 

注意我們是如何引用 other.#x 和 other.#y 的。為了訪問私人欄位,我們假設 other 是 Point 類的執行個體。

因為我們使用了 #Hash標籤 文法,它告訴 JavaScript 編譯器我們正在從當前類中尋找私人屬性。

那麼,如果不用 #Hash標籤,會發生什麼呢?

1 equals(otherPoint) {2   return this.x === otherPoint.x && this.y === otherPoint.y;3 }

 

現在我們有個問題:我們怎麼知道 otherPoint 是什嗎?

JavaScript 沒有靜態類型系統,所以 otherPoint 什麼都可能是。

有兩個原因導致這個問題:

  1. 我們的函數行為依賴於傳入的實值型別,不同的實值型別導致不同的行為:有時候是在訪問私人屬性,有時候又是在尋找公用屬性。

  2. 我們每次檢查 otherPoint 的類型:

    if (otherPoint instanceof Point && isNotSubClass(otherPoint, Point)) { return getPrivate(otherPoint, ‘foo‘); } else { return otherPoint.foo; }

更糟糕的是,如果我們引用一個私人屬性的話,就不得不在類中對每次屬性訪問進行這樣的檢查。

訪問屬性已經非常慢了,我們絕不想讓它變得更慢。

TL;DR: 我們需要對私人屬性使用 #Hash標記,因為不正常的使用標準屬性訪問機制會導致意外行為,並造成巨大的效能問題。

將私人屬性添加到語言中是件非常好的事情。感謝努力工作在 TC39 勤勞的人們,他們正在讓這美好的事情發生!

著作權聲明本譯文僅用於學習、研究和交流目的,歡迎非商業轉載。轉載請註明出處、譯者和眾成翻譯的完整連結。要擷取包含以上資訊的本文Markdown源文本,請點擊這裡。關注更多學習資料分享!

JavaScript 的新特性:類的 #private 欄位

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.