你不知道的ES6系列: 加強版Array、Object之Set、Map數據結構

前言

筆者最近在深刻學習ES6中, 在寫代碼的過程當中用到了set,Map這兩個ES6新增的數據結構, 深切感覺到了它們的強大, 解決問題的能力。因而筆者決定寫一篇文章總結一下。並分享給你們,但願可以對你們有幫助。這篇文章主要給你們介紹了關於ES6學習筆記之map、set與數組、對象對比的相關資料, 下面讓咱們一塊兒走進set, Map的世界吧😄😄。html

舒適提示: Map和Set是ES6標準新增的數據類型,請根據瀏覽器的支持狀況決定是否要使用。前端

一、Map解析

JavaScript的默認對象表示方式{}能夠視爲其餘語言中的Map或Dictionary的數據結構,即一組鍵值對。 可是JavaScript的對象{}有個小問題,就是鍵必須是字符串。但實際上Number或者其餘數據類型做爲鍵也是很是合理的。 爲了解決這個問題,最新的ES6規範引入了新的數據類型Map。git

Object和Map對比

Object是字符串-值,Map是值-值es6

  • Object鍵爲string類型,Map的鍵是任意類型
  • 手動計算Object尺寸,Map.size能夠獲取尺寸
  • Map的排序是插入順序
  • Object有原型,因此映射中有一些缺省的鍵。能夠理解爲Map=Object.create(null)

基本用法

// 初始化
let key1 = 12;
let value1 = 'test';
let m1 = new Map(); // m1: Map {}
let m2 = new Map([[key1, value1]]); // m2: Map { 12 => 'test' }
複製代碼

實例的屬性

一、Map.size() 返回對象中所包含的元素個數
let m2 = new Map([['ou', 'yang'],['xing', 'hua']]);
console.log(m2.size) //輸出2
複製代碼

5種操做方法

一、set(key, value) 向對象中添加新元素
var m = new Map(); // 空Map
m.set('ouyang', 21); // 添加新的key-value
console.log(m); //Map { 'ouyang' => 21 }
m.set('ouyang', 22)
console.log(m); //Map { 'ouyang' => 22 }
複製代碼

注意:因爲一個key只能對應一個value,因此,屢次對一個key放入value,後面的值會把前面的值沖掉。github

二、get(key) 經過鍵值查找特定的數值並返回
let m2 = new Map([[12, 'yang']])
console.log(m2.get(12)) // 返回yang
複製代碼
三、has(key) 若是鍵存在則返回true,不然false
let m2 = new Map([[12, 'yang']])
console.log(m2.has(12)); // true
console.log(m2.has(13)); // false
複製代碼
四、delete(key) 經過鍵值移除相應的value值
let m2 = new Map([[12, 'yang']])
m2.delete(12)
console.log(m2) // Map {}
複製代碼
五、clear() 將這個字典中的全部元素刪除
let m2 = new Map([[12, 'yang'],['xing', 'hua']])
m2.clear()
console.log(m2) // Map {}
複製代碼

4種遍歷方法

在此以前有必要說一下迭代器的做用, 迭代器是一種特殊對象,這種對象具備如下特色:數組

1,全部對象都有一個next方法 2,每次調用next方法,都會返回一個對象,該對象包含兩個屬性,一個是value, 表示下一個將要返回的值。另外一個是done,他是一個布爾值,用來表示該迭代器是否還有數據能夠返回. 3,迭代器還會保存一個內部指針指向當前集合中的值瀏覽器

想深刻了解迭代器的點這裏bash

一、keys() 返回一個新的 Iterator 對象。它包含按照順序插入 Map 對象中每一個元素的key值。
let m2 = new Map([[12, 'yang'],['xing', 'hua']])
var iterator1 = m2.keys(); 
console.log(iterator1); //[Map Iterator] { 12, 'xing' }
console.log(iterator1.next().value); // 12
console.log(iterator1.next().value); // xing
複製代碼
二、values() 方法返回一個新的Iterator對象。它包含按順序插入Map對象中每一個元素的value值。
let m2 = new Map([[12, 'yang'],['xing', 'hua']])
console.log(m2.values()) // [Map Iterator] { 'yang', 'hua' }
複製代碼
三、entries() 方法返回一個新的包含 [key, value]的 Iterator對象,返回的迭代器的迭代順序與 Map對象的插入順序相同。
let m2 = new Map([[12, 'yang'],['xing', 'hua']])
var iterator1 = m2.entries(); 
console.log(iterator1); //[Map Iterator] { [ 12, 'yang' ], [ 'xing', 'hua' ] }
複製代碼
四、forEach() 方法將會以插入順序對 Map 對象中的每個鍵值對執行一次參數中提供的回調函數。

myMap.forEach(callback[, thisArg])數據結構

1.必要參數, callback爲每一個元素所要執行的函數。 2.可選參數, callback執行時其 this 的值。函數

callback 函數有三個參數

1.value -元素的值 2.key -元素的鍵 3.Map -當前正在被遍歷的對象

下面請看看例子

let Data = new Map();
let objKey = { num:10 };
let obj = {
  num:5
}
Data.set("a", 1);
Data.set("b", 2);
Data.set("c", 3);
Data.forEach(function (value,key) {
  console.log(value*this.num); // 輸出5 10 15
}, obj)
複製代碼

從上面的代碼能夠看出回調函數中this指向obj對象。

Map與其餘數據結構的互相轉換

1. Map 轉爲數組

Map 轉爲數組最方便的方法,就是使用擴展運算符(...)。

const myMap = new Map()
   .set(true, 7)
   .set({foo: 3}, ['abc']);
    [...myMap]  // 輸出 [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
複製代碼
2. 數組 轉爲 Map

將數組傳入 Map 構造函數,就能夠轉爲 Map。

new Map([
   [true, 7],
   [{foo: 3}, ['abc']]
 ])
 // Map {
 //   true => 7,
 //   Object {foo: 3} => ['abc']
 // }
複製代碼
3. 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 }
複製代碼

若是有非字符串的鍵名,那麼這個鍵名會被轉成字符串,再做爲對象的鍵名。

4. 對象轉爲 Map
function 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方法、filter方法,能夠實現 Map 的遍歷和過濾(Map 自己沒有map和filter方法)。

const map0 = new Map()
   .set(1, 'a')
   .set(2, 'b')
   .set(3, 'c');
 
 const map1 = new Map(
   [...map0].filter(([k, v]) => k < 3)
 );
 // 產生 Map 結構 {1 => 'a', 2 => 'b'}
 
 const map2 = new Map(
   [...map0].map(([k, v]) => [k * 2, '_' + v])
     );
 // 產生 Map 結構 {2 => '_a', 4 => '_b', 6 => '_c'}
複製代碼

寫到這裏,Map的基本用法和應用以及講完了,我相信應該能夠知足你們平常使用的需求吧。下面來看看set數據結構吧~~

二、Set解析

  1. ES6 提供了新的數據結構 Set。它相似於數組,可是成員的值都是惟一的,沒有重複的值。Set 自己是一個構造函數,用來生成Set數據結構。
  2. Set和Map相似,也是一組key的集合,但不存儲value。因爲key不能重複,因此,在Set中,沒有重複的key。

Array和Set對比

都是一個存儲多值的容器,二者能夠互相轉換,可是在使用場景上有區別。以下:

  • Array的indexOf方法比Set的has方法效率低下
  • Set不含有重複值(能夠利用這個特性實現對一個數組的去重)
  • Set經過delete方法刪除某個值,而Array只能經過splice。二者的使用方便程度前者更優
  • Array的不少新方法map、filter、some、every等是Set沒有的(可是經過二者能夠互相轉換來使用)

基本用法

const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x)) //添加元素;
for (let i of s) {
  console.log(i);
}
// 2 3 5 4
複製代碼

Set 函數能夠接受一個數組(或者具備 iterable 接口的其餘數據結構)做爲參數,用來初始化特色:

  1. 以數組爲參數, 能夠去重
  2. 向 Set 加入值的時候,不會發生類型轉換,因此4和"4"是兩個不一樣的值。
//  以數組爲參數
const set = new Set([1, 2, 3, 4, 4, '4'])
[...set]  // [1, 2, 3, 4, '4']
set.size  // 5

// 一個相似數組的帶 iterable 接口的對象
const set = new Set(document.querySelectorAll('div'))
複製代碼

Set 實例的屬性和方法

Set 實例的屬性和方法和Map 實例的屬性和方法相似,下面簡要介紹一下。

實例的屬性

一、Set.size() 返回對象中所包含的元素個數
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x)) //添加元素;
console.log(s.size) // 由於不能有重複元素因此輸出4
複製代碼

4種操做方法

因爲Set只有key,並不存儲value(或者說鍵名和鍵值是同一個值),因此相對Map少了get()方法

  1. add(value):添加某個值,返回 Set 結構自己。
  2. delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功。
  3. has(value):返回一個布爾值,表示該值是否爲Set的成員。
  4. clear():清除全部成員,沒有返回值。
const s = new Set()
s.add(1).add(2).add(2)
console.log(s) // Set { 1, 2 }

s.size // 2

s.has(1) // true
s.has(2) // true
s.has(3) // false

s.delete(2)
console.log(s) //Set { 1 }
s.size  // 1
s.has(2) // false
複製代碼

4種遍歷方法

方法名和Map的遍歷方法名同樣,下面來看用法

  1. keys():返回鍵名的遍歷器
  2. values():返回鍵值的遍歷器
  3. entries():返回鍵值對的遍歷器
  4. forEach():使用回調函數遍歷每一個成員

keys方法、values方法、entries方法返回的都是遍歷器對象。因爲 Set 結構沒有鍵名,只有鍵值(或者說鍵名和鍵值是同一個值),因此keys方法和values方法的行爲徹底一致。

const set = new Set(['a', 'b', 'c']);

for (let item of set.keys()) {
  console.log(item); // a b c
}

for (let item of set.values()) {
  console.log(item); // a b c
}

for (let item of set.entries()) {
  console.log(item);
}
// 由於鍵名和鍵值是同一個值,因此輸出下面內容
// ["a", "a"]
// ["b", "b"]
// ["c", "c"]
set.forEach((value, key) => console.log(key + ' : ' + value))
// a: a
// b: b
// c: c
複製代碼
遍歷的應用
  1. 去重
  2. 實現並集(Union)、交集(Intersect)和差集(Difference)
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 並集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// 差集
let difference = new Set([...union].filter(x => !intersect.has(x)));
// Set {1}
複製代碼

若是想在遍歷操做中,同步改變原來的 Set 結構,目前沒有直接的方法,但有兩種變通方法。

// 方法一
let set = new Set([1, 2, 3]);
set = new Set([...set].map(val => val * 2));
// set的值是2, 4, 6

// 方法二
let set = new Set([1, 2, 3]);
set = new Set(Array.from(set, val => val * 2));
// set的值是2, 4, 6
複製代碼
注意: Set使用上須要警戒的地方

因爲Set中元素的獨一無二,根據內存地址來進行判斷,因此若是有多個元素是引用型的話,儘管值相同,可是內存地址不一樣,那麼在Set對象中也將會存儲多份,和Map相似

Set與其餘數據結構的互相轉換

1. Set集合轉化Array數組
let set = new Set([1, 2, 3, 3, 4]);
let arr = Array.from(set)  //輸出[1,2,3,4]
複製代碼
2. Array數組轉Set集合
var arr = [55, 44, 65];
var set = new Set(arr);
console.log(set) // Set { 55, 44, 65 }
console.log(set.size === arr.length);
console.log(set.has(65));
複製代碼

總結

經過筆者的解析,發現Set、Map這兩種ES6新增的數據結構在必定程度解決了Object和Array的一些痛點。他讓咱們在選擇數據結構去解決具體問題時又多了兩種選擇,在開發過程當中,涉及到數據結構,能使用Map不使用數組,尤爲是複雜的數據結構。若是數據的要求比較高,強調惟一性,就使用Set,放棄使用Object作存儲。在數據結構中優先考慮Map、Set,放棄數組和Object。不過何時使用仍是要看應用場景的啦😄。但願這篇文章可以對你們有所幫助😄。

最後允許小生附上個人github地址 裏面記錄了我學習前端的點點滴滴,以爲有幫助的小哥哥小姐姐能夠給個小星星喲😄

參考文獻
Javascript官方文檔
廖雪峯的官方網站
set和Map阮一峯教程

相關文章
相關標籤/搜索