JavaScript中Map的使用方法


創建Map

(1)使用Map構造函數創建映射對象(可傳入一個可迭代對象,需要包含鍵/值對數組)

const m = new Map()
const m1 = new Map([
    ['key1', 'val1'],
    ['key2', 'val2'],
    ['key3', 'val3']
])
const m2 = new Map({
    [Symbol.iterator]: function* () {
        yield ['key1', 'val1']
        yield ['key2', 'val2']
        yield ['key3', 'val3']
    }
})

映射期待的鍵/值對,無論是否提供

const m3 = new Map([[]])
console.log(m3.has(undefined))      // true
console.log(m3.get(undefined))      // undefined

 

查詢方法

(1)利用 has(key) 方法可查詢是否存在某個鍵

const m = new Map([
    ['firstName', 'Matt']
])
console.log(m.has('firstName'))     // true
console.log(m.has('lastName'))      // false

(2)利用 get(key) 方法可獲取鍵對應的值

const m = new Map([
    ['firstName', 'Matt']
])
console.log(m.get('firstName'))     // Matt
console.log(m.get('lastName'))      // undefined

 

操作方法

(1)使用 set(key, value) 方法插入鍵/值對,返回映射實例,因此可進行鏈式操作

const m = new Map().set('key1', 'val1')
    .set('key2', 'val2')
    .set('key3', 'val3')
console.log(m.size)     // 3

Map可以使用任何JavaScript數據類型作為鍵

Map中映射的值與Object類似,沒有限制

Map內部使用SameValueZero比較操作(ECMAScript規范內部定義,語言中不能使用),基本上相當於使用嚴格對象相等的標准來檢查鍵的匹配性

const m = new Map()

const functionKey = function () {}
const symbolKey = Symbol()
const objectKey = new Object()

m.set(functionKey, 'functionValue')
m.set(symbolKey, 'symbolValue')
m.set(objectKey, 'objectValue')

console.log(m.get(functionKey))     // functionValue
console.log(m.get(symbolKey))       // symbolValue
console.log(m.get(objectKey))       // objectValue

console.log(m.get(function () {}))  // undefined

用作鍵和值的對象及其他“集合”類型,在自己的內容或屬性被修改時仍然保持不變

const m = new Map()

const objKey = {},
    objVal = {},
    arrKey = [],
    arrVal = []

m.set(objKey, objVal)
m.set(arrKey, arrVal)

objKey.foo = 'foo'
objVal.bar = 'bar'
arrKey.push('foo')
arrVal.push('bar')

console.log(m.get(objKey))      // {bar: 'bar'}
console.log(m.get(arrKey))      // ['bar]

SameValueZero比較也可能導致意想不到的沖突

const m = new Map()

const a = 0 / ' ',
    b = 0 / ' ',
    pz = +0,
    nz = -0

console.log(a === b)        // false
console.log(pz === nz)      // true

m.set(a, 'foo')
m.set(pz, 'bar')

console.log(m.get(a))       // foo
console.log(m.get(nz))      // bar

(2)使用size屬性獲取映射中的鍵/值對數量

const m = new Map([
    ['key1', 'val1'],
    ['key2', 'val2'],
    ['key3', 'val3']
])

console.log(m.size)     // 3

(3)使用 delete() 方法和 clear() 方法刪除值

const m = new Map([
    ['key1', 'val1'],
    ['key2', 'val2'],
    ['key3', 'val3']
])

console.log(m.size)             // 3

console.log(m.has('key1'))      // true
m.delete('key1')
console.log(m.has('key1'))      // false

// 清除這個映射實例中所有鍵/值對
m.clear()
console.log(m.size)             // 0

 

順序與迭代

Map會維護鍵值對的插入順序

(1)映射實例提供一個迭代器(Iterator),能以插入順序生成[key, value]形式的數組

可以通過 entries() 方法(或者Symbol.iterator屬性,它引用 entries() )取得這個迭代器

const m = new Map([
    ['key1', 'val1'],
    ['key2', 'val2'],
    ['key3', 'val3']
])

console.log(m.entries === m[Symbol.iterator])     // true

for (let pair of m.entries()) {
    console.log(pair)
}
// ['key1', 'val1']
// ['key2', 'val2']
// ['key3', 'val3']

for (let pair of m[Symbol.iterator]()) {
    console.log(pair)
}
// ['key1', 'val1']
// ['key2', 'val2']
// ['key3', 'val3']

entries() 是默認迭代器,可以直接對映射實例使用擴展操作,把映射轉換為數組

const m = new Map([
    ['key1', 'val1'],
    ['key2', 'val2'],
    ['key3', 'val3']
])

console.log([...m])     // [['key1', 'val1'], ['key2', 'val2'], ['key3', 'val3']]

(2)調用映射的 forEach(callback, opt_thisArg) 方法並傳入回調,依次迭代每個鍵/值對

第一個參數:回調函數(值, 鍵)

第二個參數:回調函數內部this的值

const m = new Map([
    ['key1', 'val1'],
    ['key2', 'val2'],
    ['key3', 'val3']
])

m.forEach((val, key) => console.log(`${key} -> ${val}`))
// key1 -> val1
// key2 -> val2
// key3 -> val3

keys() 和 values() 分別返回以插入順序生成鍵和值的迭代器

const m = new Map([
    ['key1', 'val1'],
    ['key2', 'val2'],
    ['key3', 'val3']
])

for (let key of m.keys()) {
    console.log(key)
}
// key1
// key2
// key3

for (let val of m.values()) {
    console.log(val)
}
// val1
// val2
// val3

鍵和值在迭代器遍歷時是可以修改的,但映射內部的引用則無法修改

const m = new Map([
    ['key1', 'val1']
])

for (let key of m.keys()) {
    key = 'newKey'
    console.log(key)                // newKey
    console.log(m.get('key1'))      // val1
}

const keyObj = {id: 1}

const m1 = new Map([
    [keyObj, 'val1']
])

// 修改了作為鍵的對象的屬性,但對象在映射內部仍然引用相同的值
for (let key of m1.keys()) {
    key.id = 'newKey'
    console.log(key)                // {id: 'newKey'}
    console.log(m1.get(keyObj))     // val1
}
console.log(keyObj)                 // {id: 'newKey'}

 

選擇Object還是Map

(1)內存占用

Object和Map的工程級實現在不同瀏覽器間存在明顯差異,但存儲單個鍵/值對所占用的內存數量都會隨鍵的數量線性增加。批量添加或刪除鍵/值對則取決於各瀏覽器對該類型內存分配的工程實現。不同瀏覽器的情況不同,但給定固定大小的內存,Map大約可以比Object多存儲50%的鍵/值對

(2)插入性能

向Object和Map中插入新鍵/值對的消耗大致相當,不過插入Map在所有瀏覽器中一般會稍微快一點兒。對這兩個類型來說,插入速度並不會隨着鍵/值對數量而線性增加。如果代碼涉及大量插入操作,那么顯然Map的性能更佳

(3)查找速度

與插入不同,從大型Object和Map中查找鍵/值對的差異極小,但如果只包含少量鍵/值對,則Object有時候速度更快。在把Object當成數組使用的情況下(比如使用連續整數作為屬性),瀏覽器引擎可以進行優化,在內存中使用更高效的布局。這對Map來說是不可能的。對這兩個類型而言,查找速度不會隨着鍵/值對數量增加而線性增加。如果代碼涉及大量查找操作,那么某些情況下可能選擇Object更好一些

(4)刪除性能

使用delete刪除Object屬性的性能一直以來飽受詬病,目前在很多瀏覽器中仍然如此。為此,出現了一些偽刪除對象屬性的操作,包括把屬性設置為undefined或null。但很多時候,這都是一種討厭的或不適宜的折中。而對大多數瀏覽器引擎來說,Map的delete()操作都比插入和查找更快。如果代碼涉及大量刪除操作,那么毫無疑問應該選擇Map


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM