由來?,參數?,基本用法?
// JavaScript 的對象(Object),本質上是鍵值對的集合(Hash 結構),但是傳統上隻能用字元串當作鍵。這給它的使用帶來了很大的限制。
let obj = {sex: '男'}
let kk = {}
kk[obj] = 'kk'
console.log(kk) // { '[object Object]': 'kk' }
console.log(obj.toString()) // [object Object]
console.log(kk['[object Object]']) // kk
// 為了應對以上問題 map誕生
// ES6 提供了 Map 資料結構。它類似于對象,也是鍵值對的集合,但是“鍵”的範圍不限于字元串,各種類型的值(包括對象)都可以當作鍵。也就是說,
// Object 結構提供了“字元串—值”的對應,Map 結構提供了“值—值”的對應,是一種更完善的 Hash 結構實作。如果你需要“鍵值對”的資料結構,Map 比 Object 更合适。
// 自我了解: 普通的對象,鍵就是一個具體的值,但是map的鍵可以是一個引用
console.log('---------------------')
// map 的set,get,has,delete方法(添加成員,删除成員)
const m = new Map()
const o = {hello: 'hello world'}
m.set(o, '你好')
m.get(o)
console.log(m.get(o)) // 你好
console.log(m.has(o)) // true
console.log(m.delete(o)) // true
console.log(m.has(o)) // false
// map也可以接受一個數組作為參數
const n = new Map([['name','this is name'],['age', 'this is age']])
console.log(n.size) // 2
console.log(n.get('name')) // this is name
console.log(n.get('age')) // this is age
console.log(n.has('age')) //true
// map 接收一個數組未參數,實際執行的是如下的算法
const a = [['name','this is name'],['age', 'this is age']]
const b = new Map()
a.forEach(([key, value]) => {b.set(key,value)})
console.log(b.size) // 2
console.log(b.has('name')) // true
console.log(b.get('age')) // this is age
// map可接收參數擴充:事實上,不僅僅是數組,任何具有 Iterator 接口、且每個成員都是一個雙元素的數組的資料結構(詳見《Iterator》一章)
// 都可以當作Map構造函數的參數。這就是說,Set和Map都可以用來生成新的 Map。
const c = new Set([['name', 'kate'],['sex', 'woman']])
const c1 = new Map(c)
console.log(c1.size) // 2
console.log(c1.get('name')) // kate
console.log(c1.has('sex')) // true
const d = new Map([['name', 'kateother'],['sex', 'woman']])
const d1 = new Map(d)
console.log(d1.size) // 2
console.log(d1.get('name')) // kateother
console.log(d1.get('sex')) // woman
// 注意(當鍵是對象的時候):隻有對同一個對象的引用,map結構才會将其看做同一個鍵
const e = new Map()
e.set(['A'], 666)
console.log(e.get(['A'])) // undefined set和get方法,表面是針對同一個鍵,但實際上這是兩個不同的數組執行個體,記憶體位址是不一樣的,是以get方法無法讀取該鍵,傳回undefined。
const e1 = ['A']
e.set(e1,'AAAAAAAAAAAAA')
console.log(e.get(e1)) // AAAAAAAAAAAAA
// Map 的鍵實際上是跟記憶體位址綁定的,隻要記憶體位址不一樣,就視為兩個鍵。
// 這就解決了同名屬性碰撞(clash)的問題,我們擴充别人的庫的時候,如果使用對象作為鍵名,就不用擔心自己的屬性與原作者的屬性同名
// Map的鍵如果是一個簡單類型的值(數字,字元串,布爾值),隻要兩個值嚴格相等,map将其視為一個鍵。(注意:盡管NaN不嚴格相等于自身,但map将其視為同一個鍵)
console.log(+0 === -0) // true
let f = new Map()
f.set(-0, '000')
console.log(f.get(+0)) // 000
f.set(undefined, 'uuuuuu')
f.set(null, 'nnnnnnnnnn')
console.log(f.get(undefined)) // uuuuuu
console.log(f.get(null)) // nnnnnnnnnn
f.set(NaN, 'NNNNN')
f.set(NaN, 'MMMMMMM')
console.log(f.get(NaN)) // MMMMMMM
執行個體的屬性和操作方法
// map結構的執行個體有以下的屬性和操作方法
const a = new Map()
a.set('a', 'A')
a.set('b', 'B')
console.log(a.size) // 2
// Map.prototype.set(key,value)
// set方法設定鍵名key對應的鍵值為value,傳回的是整個map結構,如果key已經有值,就會去更新,沒有就會去建立
// set方法傳回的是Map對象,是以可以使用鍊式寫法
let b = a.set('c','C')
console.log(b) // Map(3) { 'a' => 'A', 'b' => 'B', 'c' => 'C' }
let c = new Map().set(1,'a').set(2,'b')
console.log(c) // Map(2) { 1 => 'a', 2 => 'b' }
// Map.prototype.get(key)
// get方法根據鍵名擷取值,如果找不到就傳回undefined
console.log(a.get('a')) // A
console.log(a.get('i')) // undefined
// Map.prototype.has(key)
// has方法傳回一個布爾值,根據key值取查找是否存在與整個map對象當中,傳回值是布爾值,存在還是不存在
console.log(a.has('a')) // true
console.log(a.has('i')) // false
console.log('-------------')
// Map.prototype.delete(key)
// delete方法會根據key值删除目前map對象中的這一項,傳回值表示是否删除成功
const flag = a.delete('a')
console.log(flag) // true
console.log(a.has('a')) // false
// Map.prototype.clear()
// clear 顧名思義是清除所有的成員,沒有傳回值
let d = new Map().set(1,1).set(2,2)
console.log(d.size) // 2
d.clear()
console.log(d.size) // 0
// Map結構提供三個周遊器生成函數和一個周遊方法
// 需要特别注意的是,Map的周遊順序就是插入順序
// Map.prototype.keys()
// Map.prototype.values()
// Map.prototype.entries()
// Map.prototype.forEach()
const map = new Map([[1,'1'],[2,'2'],[3,'3'],[4,'4'],[5,'5']])
console.log(map)
for(let key of map.keys()) {
console.log(key) // 12345 數字
}
for(let value of map.values()) {
console.log(value) // 12345 字元串
}
for(let item of map.entries()) {
console.log(item)
// [ 1, '1' ]
// [ 2, '2' ]
// [ 3, '3' ]
// [ 4, '4' ]
// [ 5, '5' ]
}
// 可以結構的
for(let [a, b] of map.entries()) {
console.log(a,b)
// 1 1
// 2 2
// 3 3
// 4 4
// 5 5
}
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
// 1 1
// 2 2
// 3 3
// 4 4
// 5 5
}
// map 結構的預設周遊器接口(Symbol.iterator屬性),就是entries方法
console.log(map[Symbol.iterator] === map.entries) // true
console.log(map.entries) // [Function: entries]
console.log(map[Symbol.iterator]) // [Function: entries]
console.log(map.entries())
// [Map Entries] {
// [ 1, '1' ],
// [ 2, '2' ],
// [ 3, '3' ],
// [ 4, '4' ],
// [ 5, '5' ]
// }
console.log('---擴充運算符---')
// 擴充運算符(…)内部使用for…of循環
console.log([...map]) //[ [ 1, '1' ], [ 2, '2' ], [ 3, '3' ], [ 4, '4' ], [ 5, '5' ] ]
console.log([...map.keys()]) // [ 1, 2, 3, 4, 5 ]
console.log([...map.values()]) // [ '1', '2', '3', '4', '5' ]
console.log([...map.entries()]) // [ [ 1, '1' ], [ 2, '2' ], [ 3, '3' ], [ 4, '4' ], [ 5, '5' ] ]
console.log('------利用map和filter對map進行過濾-------')
// 利用map和filter對map進行過濾
const kate = new Map([[1,'1'],[2,'2'],[3,'3'],[4,'4']])
console.log(kate)
const kate1 = new Map([...kate].filter(([kay,value]) => (value < 3)))
console.log(kate1)
const kate3 = new Map([...kate].map(([key,value]) => [key * 3, '*'+value]))
console.log(kate3) // Map(4) { 3 => '*1', 6 => '*2', 9 => '*3', 12 => '*4' }
const reporter = {
report: function(key, value) {
console.log("Key: %s, Value: %s", key, value);
}
};
map.forEach(function(value, key, map) {
console.log(this)
this.report(key, value);
}, reporter);
// { report: [Function: report] }
// Key: 1, Value: 1
// { report: [Function: report] }
// Key: 2, Value: 2
// { report: [Function: report] }
// Key: 3, Value: 3
// { report: [Function: report] }
// Key: 4, Value: 4
// { report: [Function: report] }
// Key: 5, Value: 5
const ff = new Map().set(1,'1').set(2,'2').set(3,'3')
// 轉換為數組
console.log([...ff]) // [ [ 1, '1' ], [ 2, '2' ], [ 3, '3' ] ]
// 數組轉換為map
console.log(new Map([[1,'1111'],[2,'222222']])) // Map(2) { 1 => '1111', 2 => '222222' }
// 轉換為對象 如果所有map的鍵都是字元串,它可以無損轉換為對象
function objZmap(params) {
let obj = {};
for (let [key,value] of params) {
obj[key] = value
}
return obj
}
const ffff1 = new Map().set('1',1).set('2',2)
console.log(objZmap(ffff1)) // { '1': 1, '2': 2 }
// 如果有非字元串的鍵名,那麼這個鍵名會被轉成字元串,再作為對象的鍵名。
const ffff12 = new Map().set(true,1).set(false,2)
console.log(objZmap(ffff12)) // { true: 1, false: 2 }
for (let a in objZmap(ffff12)) {
console.log(typeof a) // string
}
// 對象轉換為map
const aa = {'age': 45, 'sex': '男'}
console.log(Object.entries(aa)) // [ [ 'age', 45 ], [ 'sex', '男' ] ]
// Object.entries()方法傳回一個給定對象自身可枚舉屬性的鍵值對數組,
// 其排列與使用 for...in 循環周遊該對象時傳回的順序一緻(差別在于 for-in 循環還會枚舉原型鍊中的屬性)。
console.log(new Map(Object.entries(aa))) // Map(2) { 'age' => 45, 'sex' => '男' }
// map轉換為json
// 如果鍵名是字元串就考慮轉換為對象json map鍵名為非字元串可以考慮轉換為數組json
const nn = new Map().set('1',1).set('2',2)
console.log(JSON.stringify(objZmap(nn))) // {"1":1,"2":2}
const mm = new Map().set(true,1).set(false,2)
console.log(JSON.stringify([...mm])) // [[true,1],[false,2]]
// json 轉換為 map
// 正常情況下,是對象形式的,且鍵名都還是字元串
console.log(JSON.parse('{"yes": true, "no": false}')) // { yes: true, no: false }
console.log(new Map(Object.entries(JSON.parse('{"yes": true, "no": false}')))) // Map(2) { 'yes' => true, 'no' => false }
// 有一種特殊情況,整個 JSON 就是一個數組,且每個數組成員本身,又是一個有兩個成員的數組。這時,它可以一一對應地轉為 Map。
console.log(JSON.parse('[[true,7],[{"foo":3},["abc"]]]')) // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
console.log(new Map(JSON.parse('[[true,7],[{"foo":3},["abc"]]]'))) // Map(2) { true => 7, { foo: 3 } => [ 'abc' ] }