[JavaScript] private field & weak map

1. 背景

private fieldstc39 stage 3的提案,

class T {
  #x = 0;

  addOne(){
    return ++this.#x;
  }
}

const t = new T;
Reflect.ownKeys(t);  // []

private fields以#號開頭,例如,以上代碼中,#x就是一個private fields,
我們使用Reflect.ownKeys獲取實例t的所有屬性名,結(jié)果是一個空列表。

注:

v = {};

// 可枚舉屬性
v['a'] = 1;

// 不可枚舉屬性
Object.defineProperty(v, 'b', {
  value: 2,
  enumerable: false,
});

// Symbol屬性
s = Symbol('c');
v[s] = 3;

Object.keys(v);  // ['a']
Object.getOwnPropertyNames(v);  // ['a', 'b']
Object.getOwnPropertySymbols(v);  // [Symbol(c)]
Reflect.ownKeys(v);  // ["a", "b", Symbol(c)]

(1)Object.keys會獲取對象自身的可枚舉屬性。
(2)Object.getOwnPropertyNames會獲取對象自身的可枚舉屬性不可枚舉屬性。
(3)Reflect.ownKeys會獲取對象自身的可枚舉屬性,不可枚舉屬性Symbol屬性。

2. 用babel進(jìn)行編譯

babelv7.0.0-beta.48之后,增加了對private fields的支持,
我們來編譯上面的代碼,查看一下編譯結(jié)果。

(1)全局安裝@babel/cli,和@babel/core,并指定版本

$ npm i -g @babel/cli@7.0.0-beta.48 @babel/core@7.0.0-beta.48
$ babel --version
> 7.0.0-beta.48 (@babel/core 7.0.0-beta.48)

(2)新建npm項目,并初始化

$ mkdir test-private-field
$ cd test-private-field
$ npm init

(3)新建.babelrc文件

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-stage-3"
  ]
}

(4)安裝開發(fā)環(huán)境依賴

$ npm i -D \
@babel/core@7.0.0-beta.48 \
@babel/preset-env@7.0.0-beta.48 \
@babel/preset-stage-3@7.0.0-beta.48

(5)源碼index.js

class T {
  #x = 0;

  addOne(){
    return ++this.#x;
  }
}

const t = new T;
Reflect.ownKeys(t);  // []

(6)使用babel編譯,輸出到dist.js

$ babel index.js > dist.js

(7)編譯結(jié)果如下

"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _classPrivateFieldSet(receiver, privateMap, value) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to set private field on non-instance"); } privateMap.set(receiver, value); return value; }

function _classPrivateFieldGet(receiver, privateMap) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return privateMap.get(receiver); }

var T =
/*#__PURE__*/
function () {
  function T() {
    _classCallCheck(this, T);

    _x.set(this, 0);
  }

  _createClass(T, [{
    key: "addOne",
    value: function addOne() {
      return _classPrivateFieldSet(this, _x, +_classPrivateFieldGet(this, _x) + 1);
    }
  }]);

  return T;
}();

var _x = new WeakMap();

var t = new T();
Reflect.ownKeys(t); // []

我們看到private fields是用WeakMap實現(xiàn)的。

3. WeakMap

WeakMap是ECMAScript 2015的特性,它與Map是不同的。
(1)WeakMap的鍵必須是一個對象(object),
(2)并且WeakMap對其自身的鍵是弱引用的(weakly referenced)。

所謂弱引用,指的是作為WeakMap鍵的對象,一旦沒有其他對象引用它,
該鍵值對就會失效,被當(dāng)做垃圾回收。

由于WeakMap的這個特性,我們就可以用它來實現(xiàn)private fields了。

const weakMap = new WeakMap;
class T {
  constructor(){
    weakMap.set(this, {
      x: 0,
    });
  }

  addOne(){
    const fields = weakMap.get(this);
    return ++fields.x;
  }
}

const t = new T;
Reflect.ownKeys(t);  // []

以上代碼中,每次創(chuàng)建一個T的實例,都會向weakMap增加一個鍵值對,
其中,鍵為當(dāng)前創(chuàng)建的T的實例t,值為這個實例所擁有的private fields。

當(dāng)T的實例t不被其他對象引用的時候,該鍵值對會被自動回收,
而如果使用Map的話,則必須手動回收。


參考

MDN: Reflect.ownKeys
MDN: Object.getOwnPropertySymbols
babel: v7.0.0-beta.48
MDN: WeakMap

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評論 19 139
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,099評論 0 9
  • 前言 人生苦多,快來 Kotlin ,快速學(xué)習(xí)Kotlin! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,761評論 9 118
  • 對象的創(chuàng)建與銷毀 Item 1: 使用static工廠方法,而不是構(gòu)造函數(shù)創(chuàng)建對象:僅僅是創(chuàng)建對象的方法,并非Fa...
    孫小磊閱讀 2,186評論 0 3
  • 我很冷,一個很不恰當(dāng)?shù)脑~,很不符合季節(jié)的感受,腳底濕冷濕冷,仿佛回到了冬天。 我一直覺得自己的身體很有智行,...
    眼神犀利的貓閱讀 293評論 0 0

友情鏈接更多精彩內(nèi)容