ES6經常使用但被忽略的方法(第三彈)

Symbol

特性

  1. 惟一性
  • 屬性名屬於 Symbol 類型的,都是獨一無二的,能夠保證不會與其餘屬性名產生衝突。即便是兩個聲明徹底同樣的也是不相等的。
// 沒有參數的狀況
let s1 = Symbol();
let s2 = Symbol();
s1 === s2 // false

// 有參數的狀況
let s1 = Symbol('foo');
let s2 = Symbol('foo');
s1 === s2 // false
複製代碼
  1. 不能和其餘類型運算
  • Symbol 值不能與其餘類型的值進行運算,會報錯。
let sym = Symbol('My symbol');
"your symbol is " + sym
// TypeError: can't convert symbol to string `your symbol is ${sym}` // TypeError: can't convert symbol to string
複製代碼
  1. 類型轉換
  • Symbol 值能夠顯式轉爲字符串和布爾值,可是不能轉爲數值。
let sym = Symbol('My symbol');

String(sym) // 'Symbol(My symbol)'
sym.toString() // 'Symbol(My symbol)'

let sym = Symbol();
Boolean(sym) // true
!sym  // false

Number(sym) // TypeError
sym + 2 // TypeError
複製代碼

Symbol的應用

  1. 常見的惟一值
  • Symbol自己的特性就是任何兩個Symbol類型的值都不相等,因此咱們能夠在不知到命名變量時,都設置爲Symbol類型。Symbol類型不能使用new操做符,由於生成的 Symbol 是一個原始類型的值,不是對象。
  1. 私有屬性
  • 因爲Symbol類型的做爲屬性名時,遍歷對象的時候,該屬性不會出如今for...infor...of循環中,也不會被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。須要經過Object.getOwnPropertySymbols()方法,能夠獲取指定對象的全部 Symbol 屬性名。這樣就能夠把Symbol類型的屬性做爲私有屬性。
  • 新的 APIReflect.ownKeys()方法能夠返回全部類型的鍵名,包括常規鍵名和 Symbol 鍵名。
  1. 消除魔法字符串
  • 魔術字符串指的是,在代碼之中屢次出現、與代碼造成強耦合的某一個具體的字符串或者數值。咱們能夠把對應的字符串或數值設置成Symbol類型便可。
const name = {
    first: Symbol('detanx')
}
function getName(firstName) {
  switch (firstName) {
    case name.first: // 魔術字符串
      ...
  }
}

getName(name.first); // 魔術字符串
複製代碼

Symbol.for()和Symbol.keyFor()

  • Symbol.for()不會每次調用就返回一個新的 Symbol 類型的值,而是會先檢查給定的key是否已經存在,若是不存在纔會新建一個值。即若是傳入相同的key建立的值是相等的。而Symbol()每次建立的值都不相同,即便key相同。
let s1 = Symbol.for('detanx');
let s2 = Symbol.for('detanx');
s1 === s2 // true

let s1 = Symbol('detanx');
let s2 = Symbol('detanx');
s1 === s2 // false
複製代碼
  • Symbol.keyFor()方法返回一個已登記的 Symbol 類型值的key。未登記的 Symbol 值,返回undefined
let s1 = Symbol.for("detanx");
Symbol.keyFor(s1) // "detanx"

let s2 = Symbol("detanx");
Symbol.keyFor(s2) // undefined
複製代碼
  • Symbol()寫法沒有登記機制,因此每次調用都會返回一個不一樣的值。
  • Symbol.for()Symbol 值登記的名字,是全局環境的,無論有沒有在全局環境運行。

內置的Symbol值

Set 和 Map 數據結構

Set

  1. 特性
    • 相似於數組,可是成員的值都是惟一的,沒有重複的值。
    • Set函數能夠接受一個數組(或者具備 iterable 接口的其餘數據結構)做爲參數,用來初始化。
    • 內部兩個數據是否相同基本和===相似,區別是在SetNaNNaN也是相等的。
  2. 應用
    • 數組或字符串去重。
    [...new Set([1, 1, 2, 3])] // [1, 2, 3]
    
    [...new Set('ababbc')].join('') // "abc"
    複製代碼
    • 某些須要惟一性的數據,例如:用戶名,id等。
  3. Set 實例的屬性和方法
    • Set 結構的實例有如下屬性。
    Set.prototype.constructor:構造函數,默認就是Set函數。
    Set.prototype.size:返回Set實例的成員總數。
    複製代碼
    • 操做方法(用於操做數據
    Set.prototype.add(value):添加某個值,返回 Set 結構自己。
    Set.prototype.delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功。
    Set.prototype.has(value):返回一個布爾值,表示該值是否爲Set的成員。
    Set.prototype.clear():清除全部成員,沒有返回值。
    複製代碼
    • 遍歷方法(用於遍歷成員)
    Set.prototype.keys():返回鍵名的遍歷器
    Set.prototype.values():返回鍵值的遍歷器
    Set.prototype.entries():返回鍵值對的遍歷器
    Set.prototype.forEach():使用回調函數遍歷每一個成員
    複製代碼

WeakSet

  1. Set的區別
  • WeakSet 的成員只能是對象(null除外),而不能是其餘類型的值。
const ws = new WeakSet();
ws.add(1) // TypeError: Invalid value used in weak set
ws.add(Symbol())  // TypeError: invalid value used in weak set
ws.add(null)  // TypeError: invalid value used in weak set
複製代碼
  • WeakSet 中的對象都是弱引用,即垃圾回收機制不考慮WeakSet 對該對象的引用,也就是說,若是其餘對象都再也不引用該對象,那麼垃圾回收機制會自動回收該對象所佔用的內存,不考慮該對象還存在於 WeakSet之中。
  1. 方法。
WeakSet.prototype.add(value):向 WeakSet 實例添加一個新成員。
WeakSet.prototype.delete(value):清除 WeakSet 實例的指定成員。
WeakSet.prototype.has(value):返回一個布爾值,表示某個值是否在 WeakSet 實例之中。
複製代碼
  • WeakSet 沒有size屬性,沒有辦法遍歷它的成員。

Map

  • 因爲傳統的Object對象只能使用字符串看成鍵,因此新增的Map結構能夠將各類類型的值(包括對象)均可以看成鍵。
const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"
複製代碼
  1. 方法
  • 相比Set的操做方法,Map沒有add方法,新增了get方法和set方法。遍歷方法則是基本是同樣的。
Map.prototype.get(key) // 讀取key對應的鍵值,若是找不到key,返回undefined。
Map.prototype.has(key) // 返回一個布爾值,表示某個鍵是否在當前 Map 對象之中。
複製代碼
  1. 轉換
    (1) Map和數組
    • 在第二彈中也提到了Map和數組之間轉換,他們之間互轉是很方便的。
    // Map轉爲數組
    const myMap = new Map()
      .set(true, 7)
      .set({foo: 3}, ['abc']);
    [...myMap]
    // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
    
    // 數組轉爲 Map。
    new Map([
      [true, 7],
      [{foo: 3}, ['abc']]
    ])
    // Map {
    //   true => 7,
    //   Object {foo: 3} => ['abc']
    // }
    複製代碼
    (2) Map和對象
    • Map 的鍵都是字符串,它能夠無損地轉爲對象。若是有非字符串的鍵名,那麼這個鍵名會被轉成字符串,再做爲對象的鍵名。對象轉爲 Map 能夠經過Object.entries()
    // Map => Object
    function strMapToObj(strMap) {
      let obj = Object.create(null);
      for (let [k,v] of strMap) {
        obj[k] = v;
      }
      return obj;
    }
    
    const myMap = new Map()
      .set('yes', true)
      .set('no', false);
    strMapToObj(myMap)
    // { yes: true, no: false }
    
    // Object => Map
    // let obj = {"a":1, "b":2};
    let map = new Map(Object.entries(obj));
    複製代碼
  2. 應用
  • 在存儲的數據是鍵值對的時候,而且鍵名的類型多是多種類型是可使用Map結構。java中的Map結構有區別。

WeakMap

  1. Map的區別
    • WeakMap只接受對象做爲鍵名(null除外),不接受其餘類型的值做爲鍵名。
    const map = new WeakMap();
    map.set(1, 2) // TypeError: 1 is not an object!
    map.set(Symbol(), 2) // TypeError: Invalid value used as weak map key
    map.set(null, 2) // TypeError: Invalid value used as weak map key
    map.set(new Number(1), 2) // WeakMap {Number => 2}
    複製代碼
    • WeakMap的鍵名所指向的對象,不計入垃圾回收機制。一旦再也不須要這兩個對象,咱們就必須手動刪除這個引用,不然垃圾回收機制就不會釋放佔用的內存。
    const e1 = document.getElementById('foo');
    const e2 = document.getElementById('bar');
    const arr = [
      [e1, 'foo 元素'],
      [e2, 'bar 元素'],
    ];
    // 不須要 e1 和 e2 的時候
    // 必須手動刪除引用
    arr [0] = null;
    arr [1] = null;
    複製代碼
    • WeakMap 弱引用的只是鍵名,而不是鍵值。鍵值依然是正常引用。
    • 沒有遍歷操做(即沒有keys()values()entries()方法),也沒有size屬性。
    • 沒法清空,即不支持clear方法。WeakMap只有四個方法可用:get()set()has()delete()
  2. 應用
    (1) DOM 節點做爲鍵名
    • document.getElementById('logo')是一個DOM 節點,每當發生click事件,就更新一下狀態。咱們將這個狀態做爲鍵值放在 WeakMap 裏,對應的鍵名就是這個節點對象。一旦這個 DOM 節點刪除,該狀態就會自動消失,不存在內存泄漏風險。
    let myWeakmap = new WeakMap();
    
    myWeakmap.set(
      document.getElementById('logo'),
      {timesClicked: 0})
    ;
    
    document.getElementById('logo').addEventListener('click', function() {
      let logoData = myWeakmap.get(document.getElementById('logo'));
      logoData.timesClicked++;
    }, false);
    複製代碼
    (2) 部署私有屬性
    • 內部屬性是實例的弱引用,因此若是刪除實例,它們也就隨之消失,不會形成內存泄漏。
    const _counter = new WeakMap();
    const _action = new WeakMap();
    
    class Countdown {
      constructor(counter, action) {
        _counter.set(this, counter);
        _action.set(this, action);
      }
      dec() {
        let counter = _counter.get(this);
        if (counter < 1) return;
        counter--;
        _counter.set(this, counter);
        if (counter === 0) {
          _action.get(this)();
        }
      }
    }
    
    const c = new Countdown(2, () => console.log('DONE'));
    
    c.dec()
    c.dec()
    // DONE
    複製代碼
相關文章
相關標籤/搜索