深入理解ES6 007【學習筆記】

Set 集合與 Map 集合

在 JavaScript (JS) 中有一個 in 運算子,其不需要讀取物件的值就可以判斷屬性在物件中是否存在,如果存在就回傳 true。但是 in 運算子也會檢索物件的原型,只有當物件原型為 null 時使用這個方法才比較穩妥。

Set 集合

let set = new Set()
set.add(5)
set.add("5")
console.log(set.size) // 2
const key1={},key2={}
set.add(key1)
set.add(key1)
console.log(set.size) // 4
// 傳入相同值實際上會被忽略
let set1 = new Set(1,2,3,4,5,5,5,5)
console.log(set.size) //5
set1.has(5) //true
set1.has(6) // false
set1.delete(5)
set1.has(5) //false

set1.clear() // 所有元素被清除

Set 集合中不會對所儲存的值進行強制型別轉換,數字 5 和字串「5」可以作為兩個獨立元素存在(引擎內部使用之前提過的 Object.is() 來判斷),如果向 Set 中加入多個物件則它們之間彼此保持獨立。

Set 集合的 forEach 方法

forEach() 方法的回呼函式接受以下 3 個參數

  • Set 集合下一個索引位置
  • 與第一個參數相同的值
  • 被遍歷的 Set 集合本身

Set 集合的 forEach() 方法與陣列中的 forEach() 方法有一個奇怪的差別:回呼函式前兩個參數的值竟然是一樣的。實際上 Map 集合的 forEach() 的回呼函式都接受 3 個參數,前兩個分別是值和鍵名。

forEach 函式的第二個參數與陣列一樣,如果需要回呼函式中使用 this,則可以將它作為第二個參數傳入

let set = new Set([1,2])
let processor = {
  output(value){
    console.log(value)
  },
  process(dataSet){
    // 這裡使用箭頭函數,就無須再將this作為第二個參數傳入回調函數了
    dataSet.forEach(function(value){
      this.output(value)
    },this)
  }
}
processor.process(set)

儘管 Set 集合更適合用來追蹤多個值,而且又可以透過 forEach() 方法操作集合中的每一個元素,但是你不能像存取陣列元素那樣直接透過索引存取集合中的元素。如有需要,最好先將 Set 集合轉換成一個陣列

let set = new Set([1,2,3,3,3,4,5])
arrray = [...set]
console.log(array) // [1,2,3,4,5] 去重

function eliminateDuplicates(items){
  return [...new Set(items)]
}
let numbers = [1,2,3,3,3,4,5],
noDuplicates = eliminateDuplicates(numbers);
console.log(noDuplicates); // [1,2,3,4,5]

Weak Set 集合

將物件儲存在 Set 的實例中與儲存在變數中一樣,只要 Set 實例中的引用存在,垃圾回收機制就無法釋放該物件的記憶體空間,於是之前提到的 Set 型別可以被視為一個強引用的 Set 集合。如下所示

let set = new Set(),
key = {};
set.add(key);
console.log(set.size) // 1
// 移除原始引用
key = null;
console.log(set.size) // 1
// 重新取回原始引用
key = [...set][0]

舉例來說,我們使用這個 Set 儲存一個 DOM 物件,當有腳本把這個 DOM 元素刪除掉,那麼 Set 中還保留著這份引用(引起記憶體洩漏),所以 ES6 引入了一個 WeakSet 集合。WeakSet 只能儲存物件的弱引用,並且不可以儲存原始值;集合中的弱引用如果是物件的唯一引用,則會被回收並釋放相應記憶體。

// 集合支持add()/has()/delete()
let set = new WeakSet(),key = {};
// 向集合添加對象
set.add(key);
console.log(set.has(key)); // true
set.delete(key);
console.log(set.has(key)); // false
let key1 = {}, key2 = {};
set = new WeakSet([key1,key2]) // 如果數組中包含其非對象值,程序會拋出錯誤
console.log(set.has(key1)) // true
console.log(set.has(key2)) // true

// #################
// 移除對象key的最後一個強引用(Weak Set中的引用也自動移除)
let set = new Set(),
key = {};
set.add(key);
console.log(set.has(key)) // true
// 移除對象key的最後個強引用(Weak Set中的引用也自動移除)
key = null;
// 這段代碼執行過後,就無法訪問WeakSet中的key的引用了,由於我們需要向has方法傳遞一個強引用才能驗證這個弱引用是否已被移除了。
  • WeakSet
  • 如果向 add/has/delete 3 個方法傳入非物件參數會導致程式報錯
  • 不可迭代 (Iterable),不能使用 for-of 迴圈
  • 不暴露任何迭代器 keysvalues,所以無法透過程式本身來檢測其中的內容
  • 不支援 forEach
  • 不支援 size

Map (映射)

  • set
  • get
  • has
  • delete
  • clear
  • size 屬性

初始化方法

let map = new Map([["name","Nicholas"],["age",23]])

forEach((value,key,ownerMap)=>{},綁定上下文)

let map = new Map([["name","Nicholas"],["age",23]])
map.forEach(function(value,key,ownerMap){
   console.log(key+" "+value);
   console.log(ownerMap === map)
})

WeakMap (弱映射)

原理與 WeakSet 一致,鍵是對物件的弱引用,而且 key 只能是儲存物件

let map = new WeakMap(),
element = document.querySelector('.element');
map.set(element,"Original")
let value = map.get(element)
console.log(value) // Original
// 移除element元素
element.parentNode.removeChild(element);
element = null;
// 此時Weak Map集合為空

私有物件資料

function Person(name){
  this._name = name;
}
Person.prototype.getName = function(){
  return this._name;
}
// 約定前綴為下划線的屬性為私有屬性,不允許在對象實例外改變這個屬性。例如,只能通過getName()方法讀取this._name,所以它也有可能在無意間被覆寫

ES5 透過以下這種模式建立一個接近真正私有資料的物件

var Person = (function(){
  var privateData = {},
  privateId = 0;
  function Person(name){
    Object.defineProperty(this,"_id",{value:privateId++})
    privateData[this._id] = {
      name:name
    }
  }
  Person.prototype.getName = function(){
    return privateData[this._id].name
  }
  return Person
}())

呼叫 Person 建構函式時,屬性 _id 的值被加 1,這個屬性不可列舉、不可配置且不可寫入,上述程式碼最大的問題是無法主動管理,由於無法取得物件實例何時被銷毀,因此 privateData 中的資料就永遠不會消失。而且使用 WeakMap 集合可以解決這個問題

let Person = (function(){
  let privateData = new WeakMap();
  function Person(name){
    privateData.set(this,{name:name})
  }
  Person.prototype.getName = function(){
    return privateData.get(this).name
  }
  return Person
}())
// 只要對象實例被銷毀,相關信息也會被銷毀,從而保證了信息的私有性

只要物件實例被銷毀,相關資訊也會被銷毀,從而保證了資訊的私有性

主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://www.walker-learn.xyz/archives/4333

(0)
Walker的頭像Walker
上一篇 2025年3月8日 12:51
下一篇 2025年3月8日 12:51

相關推薦

  • Node深入淺出(聖思園教育) 003【學習筆記】

    WebSocket 與 SSE 總覽 WebSocket 基礎 定位:WebSocket 是一種在 HTTP 握手後升級的全雙工連線,允許用戶端與伺服器在同一 TCP 通道上雙向推送資料,省去了重複輪詢。 握手流程: 用戶端透過 Upgrade: websocket 標頭發起 HTTP 請求; 伺服器回應 101 Switching Protocols,雙方協…

    個人 2025年11月24日
    41700
  • 深入理解ES6 001【學習筆記】

    區塊作用域繫結 之前的變數宣告 `var` 無論在哪裡宣告,都會被視為作用域頂部宣告。由於函式是一等公民,因此順序通常是 `function 函式名稱()`、`var 變數`。 區塊宣告 區塊宣告用於宣告在指定區塊的作用域之外無法存取的變數。區塊作用域存在於: 函式內部 區塊中(字元 `{` 和 `}` 之間的區域) 暫時性死區 JavaScript 引擎在掃描程式碼發現變數宣告時,要麼會將它們提升至作…

    個人 2025年3月8日
    1.8K00
  • 【開篇】

    我是Walker,生於八十年代初,程式碼與生活的旅者。全端開發工程師,遊走於前端與後端的邊界,執著於技術與藝術的交會點。程式碼,是我編織夢想的語言;專案,是我刻畫未來的畫布。在鍵盤的敲擊聲中,我探索技術的無限可能,讓靈感在程式碼裡永恆綻放。深度咖啡愛好者,迷戀每一杯手沖的詩意與儀式感。在咖啡的醇香與苦澀中,尋找專注與靈感,亦如在開發的世界中追求極致與平衡。騎…

    2025年2月6日 個人
    2.4K00
  • 深入理解ES6 003【學習筆記】

    函數參數預設值,以及一些關於 arguments 物件,如何使用運算式作為參數、參數的暫時性死區。 以前設定預設值總是利用在含有邏輯或運算子的運算式中,前一個值是 false 時,總是回傳後面那個值,但如果我們給參數傳入 0 時,就會有些麻煩。 需要去驗證一下型別 function makeRequest(url,timeout,callback){ timeout = t…

    個人 2025年3月8日
    1.3K00
  • 深入理解ES6 008【學習筆記】

    迭代器(Iterator)和產生器(Generator)這項新特性對於高效的資料處理而言是不可或缺的,你也會發現在語言的其他特性中也都有迭代器的蹤影:新的 for-of 迴圈、展開運算子 (...)、甚至連非同步程式設計都可以使用迭代器。 迭代器是一種特殊的物件,它具有一些專門為迭代過程設計的專有介面,所有的迭代器物件都有一個 next() 方法,每次呼叫都傳回一個結果對…

    個人 2025年3月8日
    1.2K00
簡體中文 繁體中文 English