ES6私有變量的實現

1. 約定前端

實現閉包

class Example {
 constructor() {
   this._private = 'private';
 }
 getName() {
   return this._private
 }
}

var ex = new Example();

console.log(ex.getName()); // private
console.log(ex._private); // private

優勢函數

  • 寫法簡單
  • 調試方便
  • 兼容性好

缺點性能

  • 外部能夠訪問和修改
  • 語言沒有配合的機制,如 for in 語句會將全部屬性枚舉出來
  • 命名衝突

2. 閉包學習

實現一優化

/**
 * 實現一
 */
class Example {
 constructor() {
  var _private = '';
  _private = 'private';
  this.getName = function() {return _private}
 }
}
 
var ex = new Example();
 
console.log(ex.getName()); // private
console.log(ex._private); // undefined

前端全棧學習交流圈:866109386,面向1-3經驗年前端開發人員,幫助突破技術瓶頸,提高思惟能力,羣內有大量PDF可供自取,更有乾貨實戰項目視頻進羣免費領取。this

優勢編碼

  • 無命名衝突
  • 外部沒法訪問和修改

缺點調試

  • constructor 的邏輯變得複雜。構造函數應該只作對象初始化的事情,如今爲了實現私有變量,必須包含部分方法的實現,代碼組織上略不清晰。
  • 方法存在於實例,而非原型上,子類也沒法使用 super 調用
  • 構建增長一點點開銷

實現二code

/**
 * 實現二
 */
const Example = (function() {
 var _private = '';
 
 class Example {
  constructor() {
   _private = 'private';
  }
  getName() {
   return _private;
  }
 }
 
 return Example;
 
})();
 
var ex = new Example();
 
console.log(ex.getName()); // private
console.log(ex._private); // undefined

優勢

  • 無命名衝突
  • 外部沒法訪問和修改

缺點

  • 寫法有一點複雜
  • 構建增長一點點開銷

3. Symbol

實現

const Example = (function() {
  var _private = Symbol('private');
 
  class Example {
    constructor() {
     this[_private] = 'private';
    }
    getName() {
     return this[_private];
    }
  }
 
  return Example;
})();
 
var ex = new Example();
 
console.log(ex.getName()); // private
console.log(ex.name); // undefined

前端全棧學習交流圈:866109386,面向1-3經驗年前端開發人員,幫助突破技術瓶頸,提高思惟能力,羣內有大量PDF可供自取,更有乾貨實戰項目視頻進羣免費領取。

優勢

  • 無命名衝突
  • 外部沒法訪問和修改
  • 無性能損失

缺點

  • 寫法稍微複雜
  • 兼容性也還好

4. WeakMap

實現

/**
 * 實現一
 */
const _private = new WeakMap();
 
class Example {
 constructor() {
  _private.set(this, 'private');
 }
 getName() {
   return _private.get(this);
 }
}
 
var ex = new Example();
 
console.log(ex.getName()); // private
console.log(ex.name); // undefined

若是這樣寫,你可能以爲封裝性不夠,你也能夠這樣寫:

/**
 * 實現二
 */
const Example = (function() {
 var _private = new WeakMap(); // 私有成員存儲容器
 
 class Example {
  constructor() {
   _private.set(this, 'private');
  }
  getName() {
    return _private.get(this);
  }
 }
 
 return Example;
})();
 
var ex = new Example();
 
console.log(ex.getName()); // private
console.log(ex.name); // undefined

優勢

  • 無命名衝突
  • 外部沒法訪問和修改

缺點

  • 寫法比較麻煩
  • 兼容性有點問題
  • 有必定性能代價

5. 最新提案

class Point {
 #x;
 #y;
 
 constructor(x, y) {
  this.#x = x;
  this.#y = y;
 }
 
 equals(point) {
  return this.#x === point.#x && this.#y === point.#y;
 }
}

那麼爲何不直接使用 private 字段呢?好比說這樣:

class Foo {
 private value;
 
 equals(foo) {
  return this.value === foo.value;
 }
}

簡單點來講,就是嫌麻煩,固然也有性能上的考慮……

舉個例子,若是咱們不使用 #,而是使用 private 關鍵字:

class Foo {
 private value = '1';
 
 equals(foo) {
  return this.value === foo.value;
 }
}
 
var foo1 = new Foo();
var foo2 = new Foo();
 
console.log(foo1.equals(foo2));

在這裏咱們新建了兩個實例,而後將 foo2 做爲參數傳入了 foo1 的實例方法中。

那麼咱們能夠獲取 foo2.value 的值嗎?若是咱們直接 foo2.value 確定是獲取不到值的,畢竟是私有變量,但是 equals 是 Foo 的一個類方法,那麼能夠獲取到的嗎?

答案是能夠的。

其實這點在其餘語言,好比說 Java 和 C++ 中也是同樣的,類的成員函數中能夠訪問同類型實例的私有變量,這是由於私有是爲了實現「對外」的信息隱藏,在類本身內部,沒有必要禁止私有變量的訪問,你也能夠理解爲私有變量的限制是以類爲單位,而不是以對象爲單位,此外這樣作也能夠爲使用者帶來便利。

既然獲取值是能夠的,那麼打印的結果應該爲 true,可是若是咱們傳入的值不是 Foo 的實例,而是一個其餘對象呢?

var foo1 = new Foo();
 
console.log(foo1.equals({
 value: 2
}));

前端全棧學習交流圈:866109386,面向1-3經驗年前端開發人員,幫助突破技術瓶頸,提高思惟能力,羣內有大量PDF可供自取,更有乾貨實戰項目視頻進羣免費領取。

固然這裏代碼也是能夠正常運行的,可是對於編譯器來講,就有一點麻煩了,由於編譯器不知道 value 究竟是 foo 的正常屬性仍是私有屬性,因此編譯器須要作判斷,先判斷 foo 是否是 Foo 的實例,而後再接着獲取值。

這也意味着每次屬性訪問都須要作這樣一個判斷,而引擎已經圍繞屬性訪問作了高度優化,懶得改,並且還下降速度。

不過除了這個工做以外,還會有一些其餘的內容須要考慮,好比說:

  • 你必須將私有的 key 編碼進每一個詞法環境
  • for in 能夠遍歷這些屬性嗎?
  • 私有屬性和正常屬性同名的時候,誰會屏蔽誰?
  • 怎麼防止私有屬性的名稱不被探測出來。

關於使用 # 而不使用 private 更多的討論能夠參考這個Issue。

固然這些問題均可以被解決啦,就是麻煩了點。

而若是你選擇 #,實現的方式將跟 JavaScript 對象屬性徹底沒有關係,將會使用 private slots 的方式以及使用一個新的 slot 查找語法,總之就是會比 private 的實現方式簡單不少。

相關文章
相關標籤/搜索