在看完前三天的 Array & Object 组合技,感觉只要这两个东西练得够熟,应该就可以 cover 大部分关於资料储存结构的问题了。
或许一般教学 Javascript,的确是到这就差不多了,但这系列文是不断迈向更「好」的,所以我们再让自己多学一点!
因为你看到了吗?Array 跟 Object 他们也在进化啦!
Set 中文可以翻成「集合」,数学上的那个「集合」,所以 Set 可以比较容易做到「交集」、「联集」、「差集」等动作。
Set 的重点是「元素不可重复」,语法像下方这样,需要用 new 关键字来产生一个 Set,如果有初始值则带入一个 Array 到参数内:
new Set([iterable]);
Set 可以说是 Array 的进化版,但主要有两点跟 Array 不同:
听起来这进化幅度有点小欸,第二点是不是退化了啊?而且就只是从「可重复」变成「不可重复」而已,那我也可以在 Array 里面判断是否重复呀:
const arr = ['Susan', 'Allen', 'Jack'];
const newItem = 'Allen';
if (!arr.includes(newItem)) {
arr.push(newItem);
}
是的,看起来就是多了个 if 判断而已,但如果我有很多个地方都要做 push
的动作,甚至不只是 push,连 unshift
、splice
等 method 也都会有新增元素到阵列的动作,那我的 if 判断式不就加不完了?
就算真的都克服了上述的麻烦,写出来的程序也的确是「对」的,但如果换另一个工程师接手维护,光用看的也没办法立刻知道,哪个 Array 会重复,哪个 Array 不会重复。
上述的感觉就像是:我接手了一份 code,里面全都用
let
来宣告变数,完全没有const
。程序一定能跑没问题,但我就完全不知道哪些变数会变动、哪些不会。
也就是说,Array 这个进化其实不是功能上的进化,而是可读性与可维护性的进化。
最简单的用法,就是把一个带有重复元素的阵列,丢进去 Set,然後再透过 spread oparator 转回 Array:
const arr = ['Susan', 'Allen', 'Jack', 'Allen'];
const arrSet = new Set(arr);
const uniqueArray = [...arrSet];
console.log(uniqueArray);
执行结果
['Susan', 'Allen', 'Jack']
比如我是个送货员,手上有今天所有要配送的订单,但有些地址是重复的,我想要到一个地点就把当地所有货送完。
const arr = [
{ customer: 'Allen', address: '新北市政府'},
{ customer: 'Susan', address: '台大医院'},
{ customer: 'Jack', address: '板桥高铁站'},
{ customer: 'Alice', address: '新北市政府'},
];
const arrSet = new Set(arr.map(item => item.address));
console.log(`今天总共要送 ${arrSet.size} 个地方`);
console.log(Array.from(arrSet).join('、'));
执行结果
3
新北市政府、台大医院、板桥高铁站
大哉问,我第一次接触到 Set 的时候,虽然知道这是为了模拟数学上的「集合」,但,「集合」应该是没有顺序的才对吧?
关於顺序的部分,有发现吗?上面两个范例中,虽然我都没有办法使用像是 arrSet[0]
之类,透过 index 存取的语法,看起来好像 Set 没有顺序的概念,但当我透过 [...arrSet]
或者 Array.from(arrSet)
转换成 Array 时,它的顺序都跟原本一样。
代表 Javascript 的 Set 其实是有纪录顺序的(可参考 MDN,但很不明显,再补充个 StackOverflow):
new Set(arr)
或 Array.from(arr)
的 arr 顺序Set.add()
新增的元素顺序因此,虽然 Set 比 Array 少了 index 的操作,但却帮我们处理掉「重复」的问题,而这也是我们真正要使用它的原因,可以透过在 Array 跟 Set 之间切换(当然不要太频繁),把 Array 最怕的重复问题解决掉。
当我们使用 Set.add()
指令时,Javascript 背後是如何判断有没有重复的呢?
背後其实是使用严格相等(===)
也就是说,如果 Set 里面放的是 non-primitive 的元素要特别小心,因为 non-primitive 是 by reference 来判断,所以通常都会当作「不重复」处理(因为记忆体位址不同):
const arr = [
{ customer: 'Allen', address: '新北市政府'},
{ customer: 'Allen', address: '新北市政府'},
];
const arrSet = new Set(arr);
console.log(arrSet);
arrSet.add({ customer: 'Allen', address: '新北市政府'});
console.log(arrSet);
执行结果
Set(2) {
{ customer: 'Allen', address: '新北市政府'},
{ customer: 'Allen', address: '新北市政府'}
}
Set(3)) {
{ customer: 'Allen', address: '新北市政府'},
{ customer: 'Allen', address: '新北市政府'},
{ customer: 'Allen', address: '新北市政府'}
}
另外,NaN 和 undefined 都可以被放置在 Set 中(尽管 NaN !== NaN)。
Map 跟 Object 很像,使用起来最主要的差别是:
new map([iterable]);
虽然 key 可以用 Object、Array、甚至 function,但因为 key 不能重复,仍然会回到「如何判断 key 有没有重复」的问题。
答案跟前面介绍的 Set 非常像,就是 严格相等(===) 来判断,所以如果要用 non-primitive 来当 key 要特别小心:
const personMap = new Map();
personMap.set({
height: 173,
weight: 63
}, 'Joey');
// 可能。。。有个身高体重跟 Joey 一模一样的 Susan
personMap.set({
height: 173,
weight: 63
}, 'Susan');
console.log(personMap.size);
console.log(personMap);
执行结果
2
Map(2) {{…} => "Joey", {…} => "Susan"}
补充,上面的范例也可以改写成这样,直接给初始值,初始值要是 Array of Array:
const personMap = new Map([
[{ height: 173, weight: 63 }, 'Joey'],
[{ height: 173, weight: 63 }, 'Susan']
]);
console.log(personMap.size);
console.log(personMap);
Object 跟 Map 很像,但在 key/value 的顺序方面,Object 的 key/value 基本上还是会根据设置(set)的顺序,但是根据 MDN 的说法,它的排序还是有一些复杂因素影响(起码如果数字的 key 就不会按照 set 顺序),所以尽量不要相信 Object key 的顺序XD
但 Map 就不一样了,会按照 key/value 的 set 顺序,可以透过 Object.keys
/ Object.values
/ Object.entries
看到这些迹象。
比如说,我们要帮学生设定的 key/value,分别用 座号/名字:
const studentObj = {};
studentObj['22'] = 'Joey';
studentObj['33'] = 'Allen';
studentObj['11'] = 'Susan';
console.log(Object.keys(studentObj));
const studentMap = new Map();
studentMap.set('22', 'Joey');
studentMap.set('33', 'Allen');
studentMap.set('11', 'Susan');
console.log(studentMap.keys());
执行结果
["11", "22", "33"]
MapIterator {"22", "33", "11"}
Map 跟 Object 很像,一些基本的情境其实两边可以互相取代,但比较适合 Map 的情境是:
Set 跟 Map 算是比较特殊用途才会出现的,因此说真的就算直接无视它们也是活得好好的,不过多学一点总是好的,因为当使用 Object、Array 的过程中觉得「好像哪里卡卡的」的时候,就会有一道光降临到头顶上,会有个长着翅膀的人飞过来问你,「要不试试 Set 或 Map?」
独一无二的个体
即便外表相似
灵魂仍在不同的地址
Set MDN
Map MDN
es6-map-vs-object-what-and-when
why-does-js-keep-insertion-order-in-set
>>: [想试试看JavaScript ] 各种事件处理 (二)
大家好,我是乌木白,今天我们开始讲我们这次铁人赛的第一个技能,就是Git啦!先和大家声明我是把我自...
前面繁琐的设定请参考Mark飞大大的文章 然而在Mark飞大大文章的 5.接收阶段, 因为flutt...
print 输出指令 ?print Docstring: print(value, ..., sep...
今天笔者遇到一个问题,系统在开机时卡在 “Loading initial ramdisk”,用了一些...
tags: ItIron2021 Javascript 前言 昨天我们简单的带过IIFE,今天的主题...