(九)Set 和 Map 數(shù)據(jù)結(jié)構(gòu)

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);
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末荆针,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子颁糟,更是在濱河造成了極大的恐慌航背,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棱貌,死亡現(xiàn)場離奇詭異玖媚,居然都是意外死亡,警方通過查閱死者的電腦和手機婚脱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門今魔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人起惕,你說我怎么就攤上這事涡贱。” “怎么了惹想?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵问词,是天一觀的道長。 經(jīng)常有香客問我嘀粱,道長激挪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任锋叨,我火速辦了婚禮垄分,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘娃磺。我一直安慰自己薄湿,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著豺瘤,像睡著了一般吆倦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上坐求,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天蚕泽,我揣著相機與錄音,去河邊找鬼桥嗤。 笑死须妻,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泛领。 我是一名探鬼主播荒吏,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼师逸!你這毒婦竟也來了司倚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤篓像,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后皿伺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體员辩,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年鸵鸥,在試婚紗的時候發(fā)現(xiàn)自己被綠了奠滑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡妒穴,死狀恐怖宋税,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情讼油,我是刑警寧澤杰赛,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站矮台,受9級特大地震影響乏屯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瘦赫,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一辰晕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧确虱,春花似錦含友、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扎唾。三九已至,卻和暖如春南缓,著一層夾襖步出監(jiān)牢的瞬間胸遇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工汉形, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纸镊,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓概疆,卻偏偏與公主長得像逗威,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子岔冀,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355