天天看點

es6 javascript的map資料結構

1 Map 結構的目的和基本用法

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

var data = {};
var element = document.getElementById('myDiv');
data[element] = 'metadata';
data['[object HTMLDivElement]'] // "metadata"
           

上面代碼原意是将一個 DOM 節點作為對象data的鍵, 但是由于對象隻接受字元串作為鍵名, 是以element被自動轉為字元串[object HTMLDivElement]。

為了解決這個問題, ES6 提供了 Map 資料結構。 它類似于對象, 也是鍵值對的集合, 但是“ 鍵” 的範圍不限于字元串, 各種類型的值( 包括對象) 都可以當作鍵。 也就是說, Object 結構提供了“ 字元串— 值” 的對應, Map 結構提供了“ 值— 值” 的對應, 是一種更完善的 Hash 結構實作。 如果你需要“ 鍵值對” 的資料結構, Map 比 Object 更合适。

var m = new Map();
var o = {
    p: 'Hello World'
};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
           

上面代碼使用set方法, 将對象o當作m的一個鍵, 然後又使用get方法讀取這個鍵, 接着使用delete方法删除了這個鍵。

作為構造函數, Map 也可以接受一個數組作為參數。 該數組的成員是一個個表示鍵值對的數組。

var map = new Map([
    ['name', ' 張三 '],
    ['title', 'Author']
]);
map.size // 2
map.has('name') // true
map.get('name') // " 張三 "
map.has('title') // true
map.get('title') // "Author"
           

上面代碼在建立 Map 執行個體時, 就指定了兩個鍵name和title。

Map 構造函數接受數組作為參數, 實際上執行的是下面的算法。

var items = [
    ['name', ' 張三 '],
    ['title', 'Author']
];
var map = new Map();
items.forEach(([key, value]) => map.set(key, value));
           

下面的例子中, 字元串true和布爾值true是兩個不同的鍵。

var m = new Map([
    [true, 'foo'],
    ['true', 'bar']
]);
m.get(true) // 'foo'
m.get('true') // 'bar'
           

如果對同一個鍵多次指派, 後面的值将覆寫前面的值。

let map = new Map();
map
    .set(1, 'aaa')
    .set(1, 'bbb');
map.get(1) // "bbb"
           

上面代碼對鍵1連續指派兩次, 後一次的值覆寫前一次的值。

如果讀取一個未知的鍵, 則傳回undefined。

new Map().get('asfddfsasadf')
    // undefined
           

注意, 隻有對同一個對象的引用, Map 結構才将其視為同一個鍵。 這一點要非常小心。

var map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined
           

上面代碼的set和get方法, 表面是針對同一個鍵, 但實際上這是兩個值, 記憶體位址是不一樣的, 是以get方法無法讀取該鍵, 傳回undefined。

同理, 同樣的值的兩個執行個體, 在 Map 結構中被視為兩個鍵。

var map = new Map();
var k1 = ['a'];
var k2 = ['a'];
map
    .set(k1, 111)
    .set(k2, 222);
map.get(k1) // 111
map.get(k2) // 222
           

上面代碼中, 變量k1和k2的值是一樣的, 但是它們在 Map 結構中被視為兩個鍵。

由上可知, Map 的鍵實際上是跟記憶體位址綁定的, 隻要記憶體位址不一樣, 就視為兩個鍵。 這就解決了同名屬性碰撞( clash) 的問題, 我們擴充别人的庫的時候, 如果使用對象作為鍵名, 就不用擔心自己的屬性與原作者的屬性同名。

如果 Map 的鍵是一個簡單類型的值( 數字、 字元串、 布爾值), 則隻要兩個值嚴格相等, Map 将其視為一個鍵, 包括0和 - 0。 另外, 雖然NaN不嚴格相等于自身, 但 Map 将其視為同一個鍵。

let map = new Map();
map.set(NaN, 123);
map.get(NaN) // 123
map.set(-0, 123);
map.get(+0) // 123
           

繼續閱讀