1. 程式人生 > >Set 和 Map 數據結構

Set 和 Map 數據結構

數據結構 不同 urn 結果 1.0 arr 發生 return scale

Set

Set 對象允許你存儲任何類型的 唯一值, 無論是 原始值(一共6種,string, number, boolean, undefined, null,和 es6 新增的 symbol) 還是 對象引用(Object)。

先用代碼體會下這段概念的含義,直接在 Chrome 控制臺創建一個 Set 數據類型,並因此向 Set 的實例中添加 原始值 和 對象引用,查看最終的 Set 數據機構和成員個數

技術分享

上圖中,我們依次在 Set 數據結構中存儲了5種常見的原始數據類型和2種Object數據類型,最終全部存儲成功,並且查看到有7個成員

1、語法:

new Set([iterable]);

參數:iterable

可以傳入一個 可叠代對象 用來初始化 Set 數據類型,它的所有元素將被添加到新的 Set 中。如果不指定此參數或者其值為 null,則新的Set 為空。

(可叠代對象包括:Array, String, Set, Map, TypedArray, 函數的 arguments 對象,NodeList 對象等)

返回值:

一個新的 Set 對象

我們現在依次在 Chrome 控制臺中用 可叠代對象 和 普通原始數據類型(String 屬於可叠代對象)來初始化 Set 數據類型

技術分享

再來看一個 NodeList 作為參數來初始化 Set 數據類型的小栗子:

<!
DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <
div>11</div> <div>22</div> <div>33</div> <script> var divs = document.querySelectorAll(div); var set_NodeList = new Set(divs); console.log(set_NodeList); </script> </body> </html>

輸出結果:

技術分享

2、概括:

(1)、Set 中的元素是唯一的

let arr=[1,2,3,1,‘2‘];
let list2=new Set(arr);

console.log(‘unique‘,list2);   // Set(4) {1, 2, 3, "2"}

(2)、向 Set 加入值的時候,不會發生類型轉換,所以 5 和 "5" 是兩個不同的值

let s6 = new Set();
s6.add(5);
s6.add(6);
s6.add(‘5‘);
s6.add(6);
console.log(s6);   // Set(3) {5, 6, "5"}

(3)、NaN, undefined 和 null 都可以被存儲在 Set 中,在 Set 內部, 兩個 NaN 是相等的(盡管在 es5 中 NaN != NaN)

技術分享

(4)、兩個對象總是不相等的(包括空對象)

let s8 = new Set();
s8.add({});
s8.add({});
console.log(s8);   // Set(2) {Object {}, Object {}}

s8.add({‘name‘: ‘roger‘});
s8.add({‘age‘: 27});
console.log(s8);   // Set(4) {Object {}, Object {}, Object {name: "roger"}, Object {age: 27}}

(5)、數組去重 [ ...new Set( array ) ]

let arr1 = [‘apple‘, ‘huawei‘, ‘mi‘, ‘oppo‘, ‘apple‘];
let arr2 = [...new Set(arr1)];
console.log(arr2);   // ["apple", "huawei", "mi", "oppo"]

(6)、Array.from 方法可將 Set 結構轉為數組

let s9 = new Set();
s9.add([1,2,3]);
let arr = Array.from(s9);
console.log(Array.isArray(arr));   // true

// 這就提供了去除數組重復成員的另一種方法
function dedupe(array) {
  return Array.from(new Set(array));
}
dedupe([1, 1, 2, 3]);   // [1, 2, 3]

3、遍歷操作

Set 實例的方法分為兩大類:操作方法(用於操作數據)和遍歷方法(用於遍歷成員)

(1)、四個操作方法

add (value):添加某個值,返回 Set 結構本身

delete(value):刪除某個值,返回一個 bool 值,表示是否刪除成功

clear(value):清除所有成員,沒有返回值

has(value):返回一個 bool 值,表示該值是否是 Set 的成員

另外,size 相當於數組的 length 屬性,表示 Set 成員的個數

let list = new Set ();
list.add(5);
list.add(7);
console.log(‘size‘,list.size);   // size 2

// 數組作為參數
let arr = [1,2,3,4,5];
let list1 = new Set (arr);
console.log(‘size‘,list1.size);   // size 5

// 數組去重
let list2 = new Set();
list2.add(2);
list2.add(3);
list2.add(3);
console.log(‘list2‘,list2);   // Set(2) {2, 3}

let arr3=[1,2,3,1,‘2‘];
let list3=new Set(arr3);
console.log(‘unique‘,list3);   // Set(4) {1, 2, 3, "2"}
let arr4 = [‘add‘, ‘delete‘, ‘has‘, ‘clear‘];
let list4 = new Set (arr4);

console.log(‘has‘, list4.has(‘add‘));   // has true

console.log(‘delete‘, list4.delete(‘add‘), list4);   // delete true Set(3) {"delete", "has", "clear"}

console.log(‘clear‘, list4.clear(), list4);   // clear undefined Set(0) {}

(2)、四個遍歷方法

keys():返回鍵名

values():返回鍵值

entries():返回鍵值對

forEach():使用回調函數遍歷每個成員

let arr7 = [‘one‘, ‘two‘, ‘three‘, ‘four‘, ‘five‘];
let list7 = new Set(arr7);

for (let key of list7.keys()){
    console.log(‘keys‘,key);
}

// keys one
// keys two
// keys three
// keys four
// keys five

for(let value of list7.values()){
    console.log(‘values‘,value);
}

// values one
// values two
// values three
// values four
// values five

for(let entries of list7.entries()){
    console.log(‘entries‘, entries)
}

entries (2) ["one", "one"]
entries (2) ["two", "two"]
entries (2) ["three", "three"]
entries (2) ["four", "four"]
entries (2) ["five", "five"]

list7.forEach(function(item){
    console.log(item)
})

// one
// two
// three
// four
// five

WeakSet

WeakSet 對象 與 Set 類似,也是不重復的值的集合

它與 Set 對象的區別有兩點:

? WeakSet 的成員 只能是對象,不能是其它類型的值。而 Set 對象都可以;並且,WeakSet 沒有 size 屬性

技術分享

? WeakSet 中的對象都是弱引用,如果沒有其它變量或屬性引用這個對象值,那麽這個對象值會被當垃圾回收掉。正因如此,WeakSet 對象是無法遍歷的,沒有辦法拿到它所包含的元素

1、語法:

new WeakSet([iterable]);

參數:iterable

如果傳入一個 可叠代對象, 則該對象所有的 叠代值 都會被自動添加進生成的 WeakSet 對象中

註意:初始化 WeakSet 的參數是有2個要求的

(1)、傳入的參數必須是一個 可叠代對象

(2)、可叠代對象的值必須是 叠代值

通過一個例子來理解這段話:

技術分享

2、方法

WeakSet.prototype.add(value):向 WeakSet 實例添加一個新成員。
WeakSet.prototype.delete(value):清除 WeakSet 實例的指定成員。
WeakSet.prototype.has(value):返回一個布爾值,表示某個值是否在 WeakSet 實例之中。

技術分享

Map

Map 對象保存鍵值對。任何值(包括原始值和對象)都可以作為一個鍵或值

1、語法

new Map([iterable])

參數:iterable

與 WeakSet 類似,初始化Map時,Map 的參數必須是 iterable 對象,該對象的值也必須為 iterable值

技術分享

JavaScript 的對象,本質是鍵值對的集合(Hash 結構),但是傳統上只能用字符串當鍵,這給它的使用帶來了很大的限制。

例如,在《JavaScript高級程序設計》一書中,關於 Object 類型的介紹中,有這麽一段話 “ 如果屬性名中包含會導致語法錯誤的字符,或者屬性名使用的是關鍵字或保留字,也可以使用方括號表示法。例如:”

person["first name"] = "Nicholas"; 

“ 由於 ‘first name’ 中包含一個空格,所以不能用點表示法來訪問它。然而,屬性名中是可以包含非字母非數字的,這時候就可以使用方括號表示法來訪問它們。 ”

現在有一個如下結構的對象,該如何給這樣一個對象添加和讀取新成員呢?

{
        map:contra : {description: ‘Asynchronous flow control}, 
     map:dragula: {description: ‘
Drag and drop‘},
     map:woofmark: {description: ‘
Markdown and WYSIWYG editor‘} }

我們可以按照如下方法來實現:

   var obj = {}
    // 給對象添加成員
    function add(name, meta){
        obj[‘map:‘+name] = meta
    }
    // 讀取對象成員
    function get(name){
        return obj[‘map:‘+name]
    }

    add(‘contra‘, { description: ‘Asynchronous flow control‘ })
    add(‘dragula‘, { description: ‘Drag and drop‘ })
    add(‘woofmark‘, { description: ‘Markdown and WYSIWYG editor‘ })
    
    get(‘contra‘)

但如果使用 Map 結構來實現的話,就可以簡單很多

    var map = new Map()
    map.set(‘contra‘, { description: ‘Asynchronous flow control‘ })
    map.set(‘dragula‘, { description: ‘Drag and drop‘ })
    map.set(‘woofmark‘, { description: ‘Markdown and WYSIWYG editor‘ })
    console.log(map)

結果如下:

技術分享

通過前面的例子,我們來進一步了解下Map 和 Object 之間的相似點和不同點

相似點:

? 都是鍵值對的集合(Hash 結構)

? 允許按鍵 添加、刪除一個值

? 可以刪除鍵

? 可以檢測一個鍵是否綁定了值

不同點:

? Object 的鍵只能是 字符串 或 Symbol,Map 的鍵可以是任意值

? 可以通過 size 屬性獲取 Map 的鍵值對個數,而 Object 的鍵值對個數只能手動確認

? Object 都有自己的原型 prototype,不過,從 ES5 開始,可以通過 “ map = Object.create( null ) ” 來創建一個沒有原型的對象

2、Map 實例的屬性和方法

1)、set(key, value)

set 方法設置鍵名 key 對應的鍵值 value,然後返回整個 Map 結構。如果 key 已經有值,則鍵值會被更新,否則就新生成該鍵

    const m = new Map();

    m.set(‘edition‘, 6)        // 鍵是字符串       Map(1) {"edition" => 6}
    m.set(262, ‘standard‘)     // 鍵是數值         Map(2) {"edition" => 6, 262 => "standard"}
    m.set(undefined, ‘nah‘)    // 鍵是 undefined   Map(3) {"edition" => 6, 262 => "standard", undefined => "nah"}

m.set([‘123‘], 456) // 鍵是數組 Map(4) {"edition" => 6, 262 => "standard", undefined => "nah", ["123"] => 456}
 

set 方法返回的是當前的 Map 對象,因此可以采用鏈式寫法

let map = new Map()
      .set(1, ‘a‘)
      .set(2, ‘b‘)
      .set(3, ‘c‘);
// Map(3) {1 => "a", 2 => "b", 3 => "c"}

2)、get(key)

get 方法讀取 key 對應的鍵值,如果找不到 key,返回 undefined

    const m = new Map();

    const hello = function() {console.log(‘hello‘);};
    m.set(hello, ‘Hello ES6!‘) // 鍵是函數

    m.get(hello)  // Hello ES6!
    m.get(‘world‘) // undefined

3)、size 屬性

size 屬性返回 Map 結構的成員總數

    const map = new Map()
    map.set(‘foo‘, true)
    map.set(‘bar‘, false)

    console.log(map.size)   // 2

(4)、has(key)

has 方法返回一個 bool 值,表示某個鍵是否在當前的 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

(5)、delete(key)

delete 方法刪除某個鍵,返回 true。如果刪除失敗,返回 false

const m = new Map();
m.set(undefined, ‘nah‘);
m.has(undefined)     // true

m.delete(undefined)
m.has(undefined)       // false

(6)、clear()

clear 方法清除所有成員,沒有返回值

let map = new Map();
map.set(‘foo‘, true);
map.set(‘bar‘, false);

map.size // 2
map.clear()
map.size // 0

3、遍歷方法

keys():返回鍵名的遍歷器

values():返回鍵值的遍歷器

entries():返回所有成員的遍歷器

forEach():返回Map的所有成員

技術分享

也可以使用 forEach() 方法遍歷

var myMap = new Map();
myMap.set(0, ‘Zero‘);
myMap.set(1, ‘One‘);

myMap.forEach(function(value, key){
      console.log(key+‘=‘+value)
}, myMap)

// 0=Zero
// 1=One

4、與其它數據結構的互相轉換

(1)、Map 轉為數組,使用擴展運算符(...)

var map = new Map();
map.set({‘hello‘: ‘world‘})
       .set([1, NaN, ‘china‘]);

var map_array = [...map];
console.log(map_array);

// [[{hello: "world"},  undefined], [[1, NaN, "china"],  undefined]

(2)、數組轉為Map,將數組傳入Map構造函數

var map = new Map([[‘hello‘, null],[‘world‘, undefined]]);

// Map(2) {"hello" => null, "world" => undefined}

WeakMap

WeakMap 對象是一組鍵值對的集合,且其中的鍵是弱引用的。鍵必須是對象,值可以是任意值

1、語法

new WeakMap([iterable])

參數:iterable

iterable 是一個2元數組或者可遍歷的且其元素是鍵值對的對象

var mp = new WeakMap();
mp.set({‘age‘: 27}, 2017);


//  WeakMap {{age: 27} => 2017}

mp.set(1,2);   // Uncaught TypeError: Invalid value used as weak map key

2、註意點:

(1)、WeakMap只接受對象作為鍵名(null除外),不接受其他類型的

(2)、WeakMap的鍵名所指向的對象,不計入垃圾回收機制

(3)、WeakMap 沒有遍歷操作,無法清空,也沒有 size 屬性

3、方法

WeakMap只有四個方法可用:get()、set()、has()、delete()

var wm1 = new WeakMap(),
    wm2 = new WeakMap(),
    wm3 = new WeakMap();
var o1 = {},
    o2 = function(){},
    o3 = window;

wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // value可以是任意值,包括一個對象
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // 鍵和值可以是任意對象,甚至另外一個WeakMap對象
wm1.get(o2); // "azerty"
wm2.get(o2); // undefined,wm2中沒有o2這個鍵
wm2.get(o3); // undefined,值就是undefined

wm1.has(o2); // true
wm2.has(o2); // false
wm2.has(o3); // true (即使值是undefined)

wm3.set(o1, 37);
wm3.get(o1); // 37
wm3.clear();
wm3.get(o1); // undefined,wm3已被清空
wm1.has(o1);   // true
wm1.delete(o1);
wm1.has(o1);   // false

Set 和 Map 數據結構