Set集合是一種無重復(fù)元素的列表舶替,開發(fā)者們一般不會像訪問數(shù)組元素那樣逐一訪問每個元素令境,通常的做法是檢測給定的值在某個集合中是否存在。
Map集合內(nèi)含多組鍵值對顾瞪,集合中每個元素分別存放著可訪問的健名和對它對應(yīng)的值舔庶,Map集合經(jīng)常被用于緩存頻繁取用的數(shù)據(jù)抛蚁。
Set和Map都是通過Object.is()實現(xiàn)排重的。
1惕橙、Set
1) Set 本身是一個構(gòu)造函數(shù)瞧甩,用來生成 Set 數(shù)據(jù)結(jié)構(gòu)
2) 它類似于數(shù)組,但是成員的值都是唯一的弥鹦,沒有重復(fù)的值肚逸。
Set 結(jié)構(gòu)的實例有以下屬性。
- Set.prototype.constructor:構(gòu)造函數(shù)彬坏,默認(rèn)就是Set函數(shù)朦促。
- Set.prototype.size:返回Set實例的成員總數(shù)。
const set = new Set([1, 2, 3, 4, 4]);
console.log(Set.prototype.constructor)
console.log(set.size)
四個操作方法苍鲜。
- add(value):添加某個值思灰,返回Set結(jié)構(gòu)本身。
- delete(value):刪除某個值混滔,返回一個布爾值洒疚,表示刪除是否成功。
- has(value):返回一個布爾值坯屿,表示該值是否為Set的成員油湖。
- clear():清除所有成員,沒有返回值领跛。
const set = new Set([1, 2, 3, 4, 4]);
set.add(5);
set.add(6).add(7);
// 為什么可以set.add(6).add(7);
// 作業(yè)實現(xiàn)一個這樣的代碼
console.log(set);
console.log(set.has(7));
console.log(set.delete(3));
console.log(set);
console.log(set.clear());
console.log(set);
console.log(set.size)
Set的遍歷
Set 結(jié)構(gòu)的實例有四個遍歷方法乏德,可以用于遍歷成員。
- keys():返回鍵名的遍歷器
- values():返回鍵值的遍歷器
- entries():返回鍵值對的遍歷器
- forEach():使用回調(diào)函數(shù)遍歷每個成員
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
let set = new Set(['red', 'green', 'blue']);
for (let x of set) {
console.log(x);
}
// red
// green
// blue
set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
forEach
//數(shù)組 forEach
const items = ['item1', 'item2', 'item3'];
const copy = [];
items.forEach(function(item){
copy.push(item)
});
//set forEach
forEach 在數(shù)組上的介紹
演示forEach不同
使用 Set 可以很容易地實現(xiàn)并集(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([...a].filter(x => !b.has(x)));
// Set {1}
如果想在遍歷操作中,同步改變原來的 Set 結(jié)構(gòu)矢棚,目前沒有直接的方法郑什,但有兩種變通方法。一種是利用原 Set 結(jié)構(gòu)映射出一個新的結(jié)構(gòu)蒲肋,然后賦值給原來的 Set 結(jié)構(gòu)蘑拯;另一種是利用Array.from方法。
// 方法一
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
2兜粘、WeakSet
1) WeakSet 結(jié)構(gòu)與 Set 類似申窘,也是不重復(fù)的值的集合
2) WeakSet 的成員只能是對象,而不能是其他類型的值
WeakSet 結(jié)構(gòu)有以下三個方法孔轴。
- WeakSet.prototype.add(value):向 WeakSet 實例添加一個新成員剃法。
- WeakSet.prototype.delete(value):清除 WeakSet 實例的指定成員。
- WeakSet.prototype.has(value):返回一個布爾值距糖,表示某個值是否在 WeakSet 實例之中玄窝。
1) WeakSet 沒有size屬性牵寺,沒有辦法遍歷它的成員
2) WeakSet 不能遍歷悍引,是因為成員都是弱引用恩脂,隨時可能消失,遍歷機制無法保證成員的存在趣斤,很可能剛剛遍歷結(jié)束俩块,成員就取不到了。WeakSet 的一個用處浓领,是儲存 DOM 節(jié)點玉凯,而不用擔(dān)心這些節(jié)點從文檔移除時,會引發(fā)內(nèi)存泄漏联贩。
const ws = new WeakSet();
const obj = {};
const foo = {};
ws.add(window);
ws.add(obj);
ws.has(window); // true
ws.has(foo); // false
ws.delete(window);
ws.has(window); // false
3漫仆、Map
ES6 提供了 Map 數(shù)據(jù)結(jié)構(gòu)。它類似于對象泪幌,也是鍵值對的集合盲厌,但是“鍵”的范圍不限于字符串,各種類型的值(包括對象)都可以當(dāng)作鍵祸泪。也就是說吗浩,Object 結(jié)構(gòu)提供了“字符串—值”的對應(yīng),Map 結(jié)構(gòu)提供了“值—值”的對應(yīng)没隘,是一種更完善的 Hash 結(jié)構(gòu)實現(xiàn)懂扼。如果你需要“鍵值對”的數(shù)據(jù)結(jié)構(gòu),Map 比 Object 更合適右蒲。
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
//下面兩個結(jié)果一樣嗎
m.get(o) // "content"
m.get({p: 'Hello World'})
m.has(o) // true
m.delete(o) // true
m.has(o) // false
// 下面這種模式初始化不行
// const map2 = new Map({
// 'name': '張三',
// 'title': 'Author'
// });
實例的屬性和操作方法
1)size 屬性
size屬性返回 Map 結(jié)構(gòu)的成員總數(shù)阀湿。
const map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
2)set(key, value)
const m = new Map();
m.set('edition', 6) // 鍵是字符串
m.set(262, 'standard') // 鍵是數(shù)值
m.set(undefined, 'nah') // 鍵是 undefined
set方法返回的是當(dāng)前的Map對象,因此可以采用鏈?zhǔn)?/em>寫法瑰妄。
let map = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c');
3)get(key)
get方法讀取key對應(yīng)的鍵值陷嘴,如果找不到key,返回undefined翰撑。
const m = new Map();
const hello = function() {console.log('hello');};
m.set(hello, 'Hello ES6!') // 鍵是函數(shù)
m.get(hello) // Hello ES6!
4)has(key)
has方法返回一個布爾值罩旋,表示某個鍵是否在當(dāng)前 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
遍歷方法
Map 結(jié)構(gòu)原生提供三個遍歷器生成函數(shù)和一個遍歷方法宪潮。
keys():返回鍵名的遍歷器溯警。
values():返回鍵值的遍歷器趣苏。
entries():返回所有成員的遍歷器。
forEach():遍歷 Map 的所有成員梯轻。
需要特別注意的是食磕,Map 的遍歷順序就是插入順序。
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
與其他數(shù)據(jù)結(jié)構(gòu)的互相轉(zhuǎn)換
(1)Map 轉(zhuǎn)為數(shù)組
const myMap = new Map()
.set(true, 7)
.set({foo: 3}, ['abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
(2)數(shù)組 轉(zhuǎn)為 Map
將數(shù)組傳入 Map 構(gòu)造函數(shù)喳挑,就可以轉(zhuǎn)為 Map彬伦。
new Map([
[true, 7],
[{foo: 3}, ['abc']]
])
// Map {
// true => 7,
// Object {foo: 3} => ['abc']
// }
(3)Map 轉(zhuǎn)為對象
如果所有 Map 的鍵都是字符串,它可以無損地轉(zhuǎn)為對象伊诵。
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 }
如果有非字符串的鍵名单绑,那么這個鍵名會被轉(zhuǎn)成字符串,再作為對象的鍵名曹宴。
(4)對象轉(zhuǎn)為 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}
(5)Map 轉(zhuǎn)為 JSON
Map 轉(zhuǎn)為 JSON 要區(qū)分兩種情況搂橙。一種情況是,Map 的鍵名都是字符串笛坦,這時可以選擇轉(zhuǎn)為對象 JSON区转。
function strMapToJson(strMap) {
return JSON.stringify(strMapToObj(strMap));
}
let myMap = new Map().set('yes', true).set('no', false);
strMapToJson(myMap)
// '{"yes":true,"no":false}'
另一種情況是,Map 的鍵名有非字符串弯屈,這時可以選擇轉(zhuǎn)為數(shù)組 JSON蜗帜。
function mapToArrayJson(map) {
return JSON.stringify([...map]);
}
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'
(6)JSON 轉(zhuǎn)為 Map
JSON 轉(zhuǎn)為 Map,正常情況下资厉,所有鍵名都是字符串厅缺。
function jsonToStrMap(jsonStr) {
return objToStrMap(JSON.parse(jsonStr));
}
jsonToStrMap('{"yes": true, "no": false}')
// Map {'yes' => true, 'no' => false}
但是,有一種特殊情況宴偿,整個 JSON 就是一個數(shù)組湘捎,且每個數(shù)組成員本身,又是一個有兩個成員的數(shù)組窄刘。這時窥妇,它可以一一對應(yīng)地轉(zhuǎn)為 Map。這往往是 Map 轉(zhuǎn)為數(shù)組 JSON 的逆操作娩践。
function jsonToMap(jsonStr) {
return new Map(JSON.parse(jsonStr));
}
jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
// Map {true => 7, Object {foo: 3} => ['abc']}
4活翩、 WeakMap
自己看,下次分享的小伙伴分享這塊
5翻伺、 為什么需要Set和Map
長久以來材泄,數(shù)組一直是JavaScript唯一的集合類型。(不過吨岭,一些開發(fā)者認(rèn)為非數(shù)組對象也是集合拉宗,只不過是鍵值對集合,他們的用途和數(shù)組完全不同)。JavaScript數(shù)組功能與其他語言中的一樣旦事,但在ECMAScript6標(biāo)準(zhǔn)制定以前魁巩,由于可選的集合類型有限,數(shù)組使用的又是索引姐浮,因而經(jīng)常被用于創(chuàng)建隊列和棧谷遂。如果開發(fā)者們需要使用非數(shù)值型索引,就會用非數(shù)組對象創(chuàng)建所需的數(shù)據(jù)結(jié)構(gòu)单料,而這就是Set集合與Map集合的早起實現(xiàn)埋凯。
Set集合是一種無重復(fù)元素的列表点楼,開發(fā)者們一般不會像訪問數(shù)組元素那樣逐一訪問每個元素扫尖,通常的做法是檢測給定的值在某個集合中是否存在。
這些就是數(shù)組和Set掠廓,Map本質(zhì)的區(qū)別
6换怖、 Map實戰(zhàn)
實現(xiàn)多tab切換保存內(nèi)容方案
多個tab切換的時候,要保留上次操作的歷史蟀瞧。需要保留每個模塊的state沉颂。這樣要求模塊入口的index中state及子模塊的state是相互貫通的。目前有這個功能的模塊包括【任務(wù)中心】悦污、【班級管理】铸屉、【學(xué)員管理】、【師資管理】切端、【權(quán)限管理】彻坛、【課程管理】、【輔導(dǎo)班管理】踏枣。
實現(xiàn)方案介紹 (中心思想)
每個tab的path不一樣昌屉,所以以path為key,state為value建立map茵瀑。由于每個模塊有多個子模塊的state也需要維護间驮,所以擴展為采用
path+tag
為key。tag的值原則上可以隨便定義马昨,最好以數(shù)字拓展竞帽,格式如'_1','_2'鸿捧,'_3'等等屹篓。【權(quán)限管理】樹模塊有tag='_tree'
笛谦”埃【輔導(dǎo)班管理】有tag='_1'
,可以根據(jù)子tab的type確定。
使用方法
1饥脑、 合并新state和已保存的state 恳邀。需要在constructor函數(shù)里添加一行代碼:
this.state = util.BossTabMap.getSaveState(tag, this.state);
必須在this.state初始化之后懦冰。如果是路由首頁,可以這樣使用:this.state = util.BossTabMap.getSaveState('', this.state);
constructor(props) {
super(props);
....................
this.state = {
list: [],
pager: defaultPager,
searchParams: searchParams,
loading: false,
};
............
this.state = util.BossTabMap.getSaveState('',this.state);//無tag
或:
const tag='_1';
this.state = util.BossTabMap.getSaveState(tag, this.state);
}
2谣沸、 保存當(dāng)前state刷钢。只有render中的state才是最后一次操作保留下來的,所以乳附,在rander中添加代碼:
util.BossTabMap.addSaveState('', this.state);
或util.BossTabMap.addSaveState(tag, this.state);
render() {
const self = this;
..........
util.BossTabMap.addSaveState('_tree', this.state);
return (
<div className="department-tree left">
...........
</div>
);
}
核心代碼
文件位置:/new_boss_fe/src/spa/common/util/util.jsx
/*
* tab狀態(tài)保存内地。
* 獲取已保存的state并產(chǎn)生新state:this.state = UTIL.BossTabMap.getSaveState('',this.state);
* 添加已保存state:util.BossTabMap.addSaveState('',this.state);
* 刪除記錄:tag多樣性的原因。刪除時要注意赋除。
*/
..........................
BossTabMap: {
tabsMap: new Map(),// 用來保存state
tagArr: ['_tree', '_1', '_2', '_3', '_4', '_5'],//保存tag
getTag: function (path) {
// 根據(jù)path獲取tag
const self = this;
const curTag = this.tagArr.filter(function (tag) {
return self.tabsMap.has(path + tag);
});
return curTag[0] || '';
},
setTag: function (vtag) {
// 設(shè)置tag
var tag = vtag || '';
this.tagArr.push(tag);
},
getSaveState: function (tag, state) {
// 獲取已保存的state
const path = location.hash + tag;
if (this.tabsMap.has(path)) {
return Object.assign(state, this.tabsMap.get(path));
}
return state;
},
addSaveState: function (tag, state) {
// 添加state
const path = location.hash + tag;
this.tabsMap.set(path, state);
},
delSaveTabs: function (path) {
//關(guān)閉對應(yīng)的tab時候阱缓,需要從map中去掉保存的state。
// 有tag的path如何清除呢举农?
// const path = location.hash;
// tag _tree,'_1','_2' 有一些path 人為加了tag
const self = this;
const tag = this.getTag(path);
// console.log('delSaveTabs..tabsMap..start=', this.tabsMap);
if (this.tabsMap.has(path)) {
self.tagArr.forEach((ctag) => {
// console.log('ctag=', (path+ctag));
const tpath = path + ctag;
if (self.tabsMap.has(tpath)) {
// console.log('tpath=', tpath);
self.tabsMap.delete(tpath);
}
})
self.tabsMap.delete(path);
}
// console.log('delSaveTabs..tabsMap..end=', this.tabsMap);
},
has: function (path) {
if (this.tabsMap.has(path)) {
return true;
}
return false;
},
add: function (path, state) {
this.tabsMap.set(path, state);
},
get: function (path) {
return this.tabsMap.get(path);
},
del: function (path) {
this.tabsMap.delete(path);
}
}