摘要:事實上,不僅僅是數組,任何具有接口且每個成員都是一個雙元素的數組的數據結構都可以當做構造函數的參數。返回所有成員的遍歷器需要特別注意的是,的遍歷順序就是插入順序。轉為轉為,正常情況下,所有鍵名都是字符串。
Map
JavaScript 的對象(Object),本質上是鍵值對的集合(Hash 結構),但是傳統上只能用字符串當作鍵。這給它的使用帶來了很大的限制。
為了解決這個問題,ES6 提供了 Map 數據結構。它類似于對象,也是鍵值對的集合,但是“鍵”的范圍不限于字符串,各種類型的值(包括對象)都可以當作鍵。也就是說,Object 結構提供了“字符串—值”的對應,Map 結構提供了“值—值”的對應,是一種更完善的 Hash 結構實現。如果你需要“鍵值對”的數據結構,Map 比 Object 更合適。
ES6 的 Map 類型是鍵值對的有序列表,而鍵和值都可以是任意類型。 鍵的比較使用的是Object.is() ,因此你能將 5 與 "5"set方法構造
同時作為鍵,因為它們類型不同。這與使用對象屬性作為鍵的方式(指的是用對象來模擬 Map )截然不同,因為對象的屬性會被強制轉換為字符串。
你可以調用 set() 方法并給它傳遞一個鍵與一個關聯的值,來給 Map 添加項;此后使用鍵名來調用 get() 方法便能提取對應的值。例如:
let map = new Map(); map.set("title", "Understanding ES6"); map.set("year", 2016); console.log(map.get("title")); // "Understanding ES6" console.log(map.get("year")); // 2016數組構造
依然與 Set 類似,你能將數組傳遞給 Map 構造器,以便使用數據來初始化一個 Map 。該數組中的每一項也必須是數組,內部數組的首個項會作為鍵,第二項則為對應值。因此整個Map 就被這些雙項數組所填充。例如:
let map = new Map([["name", "Nicholas"], ["age", 25]]); console.log(map.has("name")); // true console.log(map.get("name")); // "Nicholas" console.log(map.size); // 2
通過構造器中的初始化, "name" 與 "age" 這兩個鍵就被添加到 map 變量中。雖然由數組構成的數組看起來有點奇怪,這對于準確表示鍵來說卻是必要的:因為鍵允許是任意數據類型,將鍵存儲在數組中,是確保它們在被添加到 Map 之前不會被強制轉換為其他類型的唯一方法。
Map構造函數接受數組作為參數,實際上執行的是下面的算法。
const items = [ ["name", "Nicholas"] ["age", 25] ] const map = new Map(); items.forEach( ([key, value]) => map.set(key, value) );
事實上,不僅僅是數組,任何具有 Iterator 接口、且每個成員都是一個雙元素的數組的數據結構都可以當做Map構造函數的參數。這就是說,Set和Map都可以用來生成新的Map
其他構造const set = new Set([ ["foo", 1], ["bar", 2] ]); const m1 = new Map(set); m1.get("foo") // 1 const m2 = new Map([["baz", 3]]); const m3 = new Map(m2); m3.get("baz") // 3
上面代碼中,我們分別使用 Set 對象和 Map 對象,當作Map構造函數的參數,結果都生成了新的 Map 對象。
Map的屬性和方法 1、屬性Map 同樣擁有 size 屬性,用于指明包含了多少個鍵值對
2、方法 2.1、操作方法set方法設置鍵名key對應的鍵值為value,然后返回整個 Map 結構。如果key已經有值,則鍵值會被更新,否則就新生成該鍵。
const m = new Map(); m.set("edition", 6) // 鍵是字符串 m.set(262, "standard") // 鍵是數值 m.set(undefined, "nah") // 鍵是 undefined
set方法返回的是當前的Map對象,因此可以采用鏈式寫法。
let map = new Map() .set(1, "a") .set(2, "b") .set(3, "c");
get方法讀取key對應的鍵值,如果找不到key,返回undefined。
const m = new Map(); const hello = function() {console.log("hello");}; m.set(hello, "Hello ES6!") // 鍵是函數 m.get(hello) // Hello ES6!2.13、has、delete、clear
Map 與 Set 共享了幾個方法,這是有意的,允許你使用相似的方式來與 Map 及 Set 進行交互。以下三個方法在 Map 與 Set
上都存在:
has方法返回一個布爾值,表示某個鍵是否在當前 Map 對象之中。
const m = new Map(); m.set("edition", 6); m.set(262, "standard"); m.set(undefined, "nah"); m.has("edition") // true m.has("years") // false m.has(262) // true m.has(undefined) // true
delete方法刪除某個鍵,返回true。如果刪除失敗,返回false。
const m = new Map(); m.set(undefined, "nah"); m.has(undefined) // true m.delete(undefined) m.has(undefined) // false
clear方法清除所有成員,沒有返回值。
let map = new Map(); map.set("foo", true); map.set("bar", false); map.size // 2 map.clear() map.size // 02.2、遍歷方法 2.2.1遍歷器生成函數
? keys():返回鍵名的遍歷器。
? values():返回鍵值的遍歷器。
? entries():返回所有成員的遍歷器
需要特別注意的是,Map 的遍歷順序就是插入順序。
const map = new Map([ ["F", "no"], ["T", "yes"], ]); for (let key of map.keys()) { console.log(key); } // "F" // "T" for (let value of map.values()) { console.log(value); } // "no" // "yes" for (let item of map.entries()) { console.log(item[0], item[1]); } // "F" "no" // "T" "yes" // 或者 for (let [key, value] of map.entries()) { console.log(key, value); } // "F" "no" // "T" "yes" // 等同于使用map.entries() for (let [key, value] of map) { console.log(key, value); } // "F" "no" // "T" "yes"
上面代碼最后的那個例子,表示 Map 結構的默認遍歷器接口(Symbol.iterator屬性),就是entries方法。
2.2.2、forEachMap 的 forEach() 方法類似于 Set 與數組的同名方法,它接受一個能接收三個參數的回調函數:
Map 中下個位置的值;
該值所對應的鍵;
目標 Map 自身。
回調函數的這些參數更緊密契合了數組 forEach() 方法的行為,即:第一個參數是值、第二個參數則是鍵(數組中的鍵是數值索引)。此處有個示例:
let map = new Map([ ["name", "Nicholas"], ["age", 25] ]); map.forEach(function(value, key, ownerMap) { console.log(key + " " + value); console.log(ownerMap === map); });
forEach() 的回調函數輸出了傳給它的信息。其中 value 與 key 被直接輸出, ownerMap
與 map 進行了比較,說明它們是相等的。這就輸出了:
name Nicholas true age 25 trueMap與其他數據結構的相互裝換 Map轉數組:
Map 結構轉為數組結構,比較快速的方法是使用擴展運算符(...)。
const map = new Map([ [1, "one"], [2, "two"], [3, "three"], ]); [...map.keys()] // [1, 2, 3] [...map.values()] // ["one", "two", "three"] [...map.entries()] // [[1,"one"], [2, "two"], [3, "three"]] [...map] // [[1,"one"], [2, "two"], [3, "three"]]數組轉為 Map
將數組傳入 Map 構造函數,就可以轉為 Map。
new Map([ [true, 7], [{foo: 3}, ["abc"]] ]) // Map { // true => 7, // Object {foo: 3} => ["abc"] // }Map 轉為對象
如果所有 Map 的鍵都是字符串,它可以無損地轉為對象。
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 }
如果有非字符串的鍵名,那么這個鍵名會被轉成字符串,再作為對象的鍵名。
對象轉為 Mapfunction objToStrMap(obj) { let strMap = new Map(); for (let k of Object.keys(obj)) { strMap.set(k, obj[k]); } return strMap; } objToStrMap({yes: true, no: false}) // Map {"yes" => true, "no" => false}Map 轉為 JSON
Map 轉為 JSON 要區分兩種情況。一種情況是,Map 的鍵名都是字符串,這時可以選擇轉為對象 JSON。
function strMapToJson(strMap) { return JSON.stringify(strMapToObj(strMap)); } let myMap = new Map().set("yes", true).set("no", false); strMapToJson(myMap) // "{"yes":true,"no":false}"
另一種情況是,Map 的鍵名有非字符串,這時可以選擇轉為數組 JSON。
function mapToArrayJson(map) { return JSON.stringify([...map]); } let myMap = new Map().set(true, 7).set({foo: 3}, ["abc"]); mapToArrayJson(myMap) // "[[true,7],[{"foo":3},["abc"]]]"JSON 轉為 Map
JSON 轉為 Map,正常情況下,所有鍵名都是字符串。
function jsonToStrMap(jsonStr) { return objToStrMap(JSON.parse(jsonStr)); } jsonToStrMap("{"yes": true, "no": false}") // Map {"yes" => true, "no" => false}
但是,有一種特殊情況,整個 JSON 就是一個數組,且每個數組成員本身,又是一個有兩個成員的數組。這時,它可以一一對應地轉為 Map。這往往是 Map 轉為數組 JSON 的逆操作。
function jsonToMap(jsonStr) { return new Map(JSON.parse(jsonStr)); } jsonToMap("[[true,7],[{"foo":3},["abc"]]]") // Map {true => 7, Object {foo: 3} => ["abc"]}WeakMap: WeakMap的特性
WeakMap與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
其次,WeakMap的鍵名所指向的對象,不計入垃圾回收機制。
WeakMap的設計目的在于,有時我們想在某個對象上面存放一些數據,但是這會形成對于這個對象的引用。請看下面的例子。
const e1 = document.getElementById("foo"); const e2 = document.getElementById("bar"); const arr = [ [e1, "foo 元素"], [e2, "bar 元素"], ];
上面代碼中,e1和e2是兩個對象,我們通過arr數組對這兩個對象添加一些文字說明。這就形成了arr對e1和e2的引用。
一旦不再需要這兩個對象,我們就必須手動刪除這個引用,否則垃圾回收機制就不會釋放e1和e2占用的內存。
// 不需要 e1 和 e2 的時候
// 必須手動刪除引用
arr [0] = null; arr [1] = null;
上面這樣的寫法顯然很不方便。一旦忘了寫,就會造成內存泄露。
WeakMap 就是為了解決這個問題而誕生的,它的鍵名所引用的對象都是弱引用,即垃圾回收機制不將該引用考慮在內。因此,只要所引用的對象的其他引用都被清除,垃圾回收機制就會釋放該對象所占用的內存。也就是說,一旦不再需要,WeakMap 里面的鍵名對象和所對應的鍵值對會自動消失,不用手動刪除引用。
WeakMap的構造:ES6 的 WeakMap 類型是鍵值對的無序列表,其中鍵必須是非空的對象,值則允許是任意類型。 WeakMap 的接口與 Map 的非常相似
// WeakMap 可以使用 set 方法添加成員
const wm1 = new WeakMap(); const key = {foo: 1}; wm1.set(key, 2); wm1.get(key) // 2
// WeakMap 也可以接受一個數組,
// 作為構造函數的參數
const k1 = [1, 2, 3]; const k2 = [4, 5, 6]; const wm2 = new WeakMap([[k1, "foo"], [k2, "bar"]]); wm2.get(k2) // "bar"WeakMap的屬性和方法:
WeakMap 與 Map 在 API 上的區別主要是兩個,一是沒有遍歷操作(即沒有keys()、values()和entries()方法),也沒有size屬性。因為沒有辦法列出所有鍵名,某個鍵名是否存在完全不可預測,跟垃圾回收機制是否運行相關。這一刻可以取到鍵名,下一刻垃圾回收機制突然運行了,這個鍵名就沒了,為了防止出現不確定性,就統一規定不能取到鍵名。二是無法清空,即不支持clear方法。因此,WeakMap只有四個方法可用:get()、set()、has()、delete()。
const wm = new WeakMap(); // size、forEach、clear 方法都不存在 wm.size // undefined wm.forEach // undefined wm.clear // undefinedWeakMap常用場景
Weak Map 的最佳用武之地,就是在瀏覽器中創建一個關聯到特定 DOM 元素的對象。例如,某些用在網頁上的 JS 庫會維護一個自定義對象,用于引用該庫所使用的每一個 DOM 元素,并且其映射關系會存儲在內部的對象緩存中。
該方法的困難之處在于:如何判斷一個 DOM 元素已不復存在于網頁中,以便該庫能移除此元素的關聯對象。若做不到,該庫就會繼續保持對 DOM 元素的一個無效引用,并造成內存泄漏。使用 Weak Map 來追蹤 DOM 元素,依然允許將自定義對象關聯到每個 DOM 元素,而在此對象所關聯的 DOM 元素不復存在時,它就會在 Weak Map 中被自動銷毀。
必須注意的是, Weak Map 的鍵才是弱引用,而值不是。在 Weak Map 的值中存儲對象會阻止垃圾回收,即使該對象的其他引用已全都被移除。
當決定是要使用 Weak Map 還是使用正規 Map 時,首要考慮因素在于你是否只想使用對象類型的鍵。如果你打算這么做,那么最好的選擇就是
Weak Map 。因為它能確保額外數據在不再可用后被銷毀,從而能優化內存使用并規避內存泄漏。 要記住 Weak Map
只為它們的內容提供了很小的可見度,因此你不能使用 forEach() 方法、size 屬性或 clear()
方法來管理其中的項。如果你確實需要一些檢測功能,那么正規 Map會是更好的選擇,只是一定要確保留意內存的使用。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/96829.html
摘要:一個對象若只被弱引用所引用,則被認為是不可訪問或弱可訪問的,并因此可能在任何時刻被回收。也就是說,一旦不再需要,里面的鍵名對象和所對應的鍵值對會自動消失,不用手動刪除引用。如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。 前言 我們先從 WeakMap 的特性說起,然后聊聊 WeakMap 的一些應用場景。 特性 1. WeakMap 只接受對象作為鍵名 const map = ...
Set有對應的WeakSet, Map也有WeakMap。這一篇,我們就來學習一下WeakMap有寫什么特性。先來看一下WeakMap的基本特性: 1: WeakMap是一種存儲多個鍵值對的無序列表 2: WeakMap的鍵必須是非null的對象類型 3: WeakMap的鍵對應的值,可以是任意類型 接下來看一下WeakMap的接口方法:一:WeakMap的新建與初始化與Map相同的,WeakM...
摘要:返回一個布爾值,表示該值是否為的成員。返回鍵名的遍歷器返回鍵值的遍歷器返回鍵值對的遍歷器使用回調函數遍歷每個成員需要特別指出的是,的遍歷順序就是插入順序。該數組的所有成員,都會自動成為實例對象的成員。這意味著,數組的成員只能是對象。 1.Set ES6 提供了新的數據結構 Set。它類似于數組,但是成員的值都是唯一的,沒有重復的值。Set 本身是一個構造函數,用來生成 Set 數據結構...
摘要:對象保存鍵值對。清空用于移除對象中指定的元素。執行刪除操作返回一個值,用來表明中是否存在指定元素一樣的后面的會覆蓋前面的值把對象轉換為迭代器返回一個新的對象對象是一組鍵值對的集合,其中的鍵是弱引用的。其鍵必須是對象,而值可以是任意的。 Map 對象保存鍵值對。任何值(對象或者原始值) 都可以作為一個鍵或一個值。 使用映射對象 let myMap=new Map(); let keyOb...
摘要:引入的數據結構新加入的數據類型有這些數據結構的支持并不廣泛,在寫這篇文章的時候。是或其他可枚舉的對象,其每個元素是的元數組。開頭的和不對持有引用,不影響。因此,他們沒有辦法對自身的進行直接的枚舉。目前新版的和支持。 原文:http://pij.robinqu.me/JavaScript_Core/ECMAScript/es6/es6_data_types.html 源代...
閱讀 2628·2021-11-23 09:51
閱讀 2418·2021-09-30 09:48
閱讀 2044·2021-09-22 15:24
閱讀 1009·2021-09-06 15:02
閱讀 3303·2021-08-17 10:14
閱讀 1934·2021-07-30 18:50
閱讀 1980·2019-08-30 15:53
閱讀 3168·2019-08-29 18:43