我注册过的网站,seo入门培训,网站托管费,白人与黑人做爰网站文章目录 14、Set 和 Map 数据结构1. Set1.1 基本用法☆☆☆ 值唯一#xff0c;没有重复的值☆☆☆ 接受数组、具有 iterable 接口的数据结构☆☆☆ 数组去重1#xff1a;[...new Set(array)]☆☆☆ 字符串去重#xff1a;[...new Set(ababbc)].join()☆ 两个对象总是不相等… 文章目录 14、Set 和 Map 数据结构1. Set1.1 基本用法☆☆☆ 值唯一没有重复的值☆☆☆ 接受数组、具有 iterable 接口的数据结构☆☆☆ 数组去重1[...new Set(array)]☆☆☆ 字符串去重[...new Set(ababbc)].join()☆ 两个对象总是不相等的set.add({});1.2 Set实例的属性和方法-- Array.from() 与 Set☆☆☆ 数组去重2Array.from(new Set([1, 2, 3, 4, 5]));1.3 遍历操作-- values()entries()☆☆☆ Set 默认遍历器 values()-- forEach()-- 遍历的应用...、for...of☆ Set 轻易实现并集、交集、差集 2. WeakSet。。。3. Map3.1 含义和基本用法-- Map 接收二维数组作为参数任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。☆ 只有对同一个对象的引用Map 结构才将其视为同一个键3.2 实例的属性和操作方法-- size 属性-- Map.prototype.set(key, value)-- Map.prototype.get(key)-- Map.prototype.has(key)-- Map.prototype.delete(key)-- Map.prototype.clear()3.3 遍历方法☆ 结合数组实现map的过滤-- forEach()3.4 与其他数据结构的相互转换-- Map转为数组-- 数组转为Map-- Map 转为对象-- 对象转为 Map-- Map 转为 JSON-- JSON 转为 Map 4. WeakMap。。。5. WeakRef。。。6. FinalizationRegistry : ES2021引入清理器注册表功能END 14、Set 和 Map 数据结构
1. Set
1.1 基本用法
ES6ES6于2015年6月发布提供了新的数据结构 Set。它类似于数组但是成员的值都是唯一的没有重复的值。
☆☆☆ 值唯一没有重复的值
Set本身是一个构造函数用来生成 Set 数据结构。
const s new Set();[2, 3, 5, 4, 5, 2, 2].forEach(x s.add(x));for (let i of s) {console.log(i);
}
// 2 3 5 4☆☆☆ 接受数组、具有 iterable 接口的数据结构
Set函数可以接受一个数组或者具有 iterable 接口的其他数据结构作为参数用来初始化。
// 例一
const set new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]// 例二
const items new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5// 例三
const set new Set(document.querySelectorAll(div));
set.size // 56// 类似于
const set new Set();
document.querySelectorAll(div).forEach(div set.add(div));
set.size // 56上面代码中例一和例二都是Set函数接受数组作为参数例三是接受类似数组的对象作为参数。
☆☆☆ 数组去重1[…new Set(array)]
// 去除数组的重复成员
[...new Set(array)]☆☆☆ 字符串去重[…new Set(‘ababbc’)].join(‘’)
[...new Set(ababbc)].join() // Set-Array-String
// abc[...new Set(ababbc)].join( - )
// a - b - c向 Set 加入值的时候不会发生类型转换所以5和5是两个不同的值。Set 内部判断两个值是否不同使用的算法叫做“Same-value-zero equality”它类似于精确相等运算符主要的区别是向 Set 加入值时认为NaN等于自身而精确相等运算符认为NaN不等于自身。
let set new Set();
let a NaN;
let b NaN;
set.add(a);
set.add(b);
set // Set {NaN}————
☆ 两个对象总是不相等的set.add({});
let set new Set();set.add({});
set.size // 1set.add({});
set.size // 21.2 Set实例的属性和方法
Set 结构的实例有以下属性。 Set.prototype.constructor构造函数默认就是Set函数。 Set.prototype.size返回Set实例的成员总数。
Set 实例的方法分为两大类操作方法用于操作数据和遍历方法用于遍历成员。下面先介绍四个操作方法。
Set.prototype.add(value)添加某个值返回 Set 结构本身 Set.prototype.delete(value)删除某个值返回一个布尔值表示删除是否成功 Set.prototype.has(value)返回一个布尔值表示该值是否为Set的成员 Set.prototype.clear()清除所有成员没有返回值
s.add(1).add(2).add(2); // 添加某个值返回 Set 结构本身
// 注意2被加入了两次s.size // 2s.has(1) // true
s.has(2) // true
s.has(3) // falses.delete(2) // true
s.has(2) // false———— 下面是一个对比判断是否包括一个键Object结构和Set结构写法的不同。
// 对象的写法
const properties {width: 1,height: 1
};if (properties[someName]) { // 对象这样写法参考对象的拓展1// do something
}// Set的写法
const properties new Set();properties.add(width);
properties.add(height);if (properties.has(someName)) {// do something
}————
– Array.from() 与 Set
☆☆☆ 数组去重2Array.from(new Set([1, 2, 3, 4, 5]));
Array.from()方法可以将 Set 结构转为数组。–参考 “9、数组的扩展”
const items new Set([1, 2, 3, 4, 5]);
const array Array.from(items); // 这就提供了去除数组重复成员的另一种方法。
function dedupe(array) {return Array.from(new Set(array));
}dedupe([1, 1, 2, 3]) // [1, 2, 3]1.3 遍历操作
Set 结构的实例有四个遍历方法可以用于遍历成员。
Set.prototype.keys()返回键名的遍历器 Set.prototype.values()返回键值的遍历器 Set.prototype.entries()返回键值对的遍历器 Set.prototype.forEach()使用回调函数遍历每个成员
需要特别指出的是Set的遍历顺序就是 插入顺序。这个特性有时非常有用比如使用 Set 保存一个回调函数列表调用时就能保证按照添加顺序调用。
– values()entries()
keys方法、values方法、entries方法返回的都是遍历器对象详见《Iterator 对象》一章。由于 Set 结构没有键名只有键值或者说键名和键值是同一个值所以keys方法和values方法的行为完全一致。
let set new Set([red, green, blue]);for (let item of set.keys()) {console.log(item);
}
// red
// green
// bluefor (let item of set.values()) {console.log(item);
}
// red
// green
// bluefor (let item of set.entries()) {console.log(item);
}
// [red, red]
// [green, green]
// [blue, blue]☆☆☆ Set 默认遍历器 values()
Set 结构的实例默认可遍历它的默认遍历器生成函数就是它的values方法。
Set.prototype[Symbol.iterator] Set.prototype.values // 查看成员方法
// true这意味着可以省略values方法直接用for...of循环遍历 Set。
let set new Set([red, green, blue]);for (let x of set) {console.log(x);
}
// red
// green
// blue– forEach()
Set 结构的实例与数组一样也拥有forEach方法用于对每个成员执行某种操作没有返回值。
let set new Set([1, 4, 9]);
set.forEach((value, key) console.log(key : value))
// 1 : 1
// 4 : 4
// 9 : 9上面代码说明forEach方法的参数就是一个处理函数。该函数的参数与数组的forEach一致依次为键值、键名、集合本身上例省略了该参数。这里需要注意Set 结构的键名就是键值两者是同一个值因此第一个参数与第二个参数的值永远都是一样的。
另外forEach方法还可以有第二个参数表示绑定处理函数内部的this对象。
– 遍历的应用…、for…of
扩展运算符...内部使用for...of循环所以也可以用于 Set 结构。
... 调用Iterator接口才生效
let set new Set([red, green, blue]);
let arr [...set];
// [red, green, blue]数组的map和filter方法也可以间接用于 Set 了。
let set new Set([1, 2, 3]);
set new Set([...set].map(x x * 2)); // Set-array-set
// 返回Set结构{2, 4, 6}let set new Set([1, 2, 3, 4, 5]);
set new Set([...set].filter(x (x % 2) 0));
// 返回Set结构{2, 4}☆ Set 轻易实现并集、交集、差集
因此使用 Set 可以很容易地实现并集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}// a 相对于 b 的差集
let difference new Set([...a].filter(x !b.has(x)));
// Set {1}如果想在遍历操作中同步改变原来的 Set 结构目前没有直接的方法但有两种变通方法。一种是利用原 Set 结构映射出一个新的结构然后赋值给原来的 Set 结构另一种是利用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上面代码提供了两种方法直接在遍历操作中改变原来的 Set 结构。 2. WeakSet。。。 3. Map
3.1 含义和基本用法
JavaScript 的对象Object本质上是键值对的集合Hash 结构但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
const data {};
const element document.getElementById(myDiv);data[element] metadata;
data[[object HTMLDivElement]] // metadata上面代码原意是将一个 DOM 节点作为对象data的键但是由于对象只接受字符串作为键名不是还有属性名作为键值吗这是为啥所以element被自动转为字符串[object HTMLDivElement]。
为了解决这个问题ES6 提供了 Map 数据结构。它类似于对象也是键值对的集合但是“键”的范围不限于字符串各种类型的值包括对象都可以当作键。也就是说Object 结构提供了 “字符串—值” 的对应Map 结构提供了“值—值”的对应是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构Map 比 Object 更合适。
const m new Map();
const o {p: Hello World};m.set(o, content)
m.get(o) // contentm.has(o) // true
m.delete(o) // true
m.has(o) // false上面代码使用 Map 结构的set方法将对象o当作m的一个键然后又使用get方法读取这个键接着使用delete方法删除了这个键。 ————
– Map 接收二维数组作为参数任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。
上面的例子展示了如何向 Map 添加成员。作为构造函数Map 也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。
const map new Map([[name, 张三],[title, Author]
]); // 实际是一个个遍历数组将数组中的值放入mapmap.size // 2
map.has(name) // true
map.get(name) // 张三
map.has(title) // true
map.get(title) // Author上面代码在新建 Map 实例时就指定了两个键name和title。
Map构造函数接受数组作为参数实际上执行的是下面的算法。
const items [[name, 张三],[title, Author]
];const map new Map();items.forEach(([key, value]) map.set(key, value)
);事实上不仅仅是数组任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构详见《Iterator》一章都可以当作Map构造函数的参数。这就是说Set和Map都可以用来生成新的 Map。
const set new Set([[foo, 1],[bar, 2]
]);
const m1 new Map(set);
m1.get(foo) // 1const m2 new Map([[baz, 3]]); // 具有Iterator接口xxx
const m3 new Map(m2);
m3.get(baz) // 3———— 如果对同一个键多次赋值后面的值将覆盖前面的值。
const map new Map();map
.set(1, aaa)
.set(1, bbb);map.get(1) // bbb如果读取一个未知的键则返回undefined。
new Map().get(asfddfsasadf)
// undefined————
☆ 只有对同一个对象的引用Map 结构才将其视为同一个键
注意只有对同一个对象的引用Map 结构才将其视为同一个键。这一点要非常小心。
const map new Map();map.set([a], 555);
map.get([a]) // undefined上面代码的set和get方法表面是针对同一个键但实际上这是两个不同的数组实例内存地址是不一样的因此get方法无法读取该键返回undefined。
同理同样的值的两个实例在 Map 结构中被视为两个键。
const map new Map();const k1 [a];
const k2 [a];map
.set(k1, 111)
.set(k2, 222); // 键值绑定的是内存地址map.get(k1) // 111
map.get(k2) // 222上面代码中变量k1和k2的值是一样的但是它们在 Map 结构中被视为两个键。
由上可知Map 的键实际上是跟内存地址绑定的只要内存地址不一样就视为两个键。这就解决了同名属性碰撞clash的问题我们扩展别人的库的时候如果使用对象作为键名就不用担心自己的属性与原作者的属性同名。
如果 Map 的键是一个简单类型的值数字、字符串、布尔值则只要两个值严格相等Map 将其视为一个键比如0和-0就是一个键布尔值true和字符串true则是两个不同的键。另外undefined和null也是两个不同的键。虽然NaN不严格相等于自身但 Map 将其视为同一个键。
let map new Map();map.set(-0, 123);
map.get(0) // 123map.set(true, 1);
map.set(true, 2);
map.get(true) // 1map.set(undefined, 3);
map.set(null, 4);
map.get(undefined) // 3map.set(NaN, 123);
map.get(NaN) // 1233.2 实例的属性和操作方法
Map 结构的实例有以下属性和操作方法。
– size 属性
size属性返回 Map 结构的成员总数。
const map new Map();
map.set(foo, true);
map.set(bar, false);map.size // 2– Map.prototype.set(key, value)
set方法设置键名key对应的键值为value然后返回整个 Map 结构。如果key已经有值则键值会被更新否则就新生成该键。
const m new Map();m.set(edition, 6) // 键是字符串
m.set(262, standard) // 键是数值
m.set(undefined, nah) // 键是 undefinedset方法返回的是当前的Map对象因此可以采用链式写法。
let map new Map().set(1, a).set(2, b).set(3, c);– Map.prototype.get(key)
get方法读取key对应的键值如果找不到key返回undefined。
const m new Map();const hello function() {console.log(hello);};
m.set(hello, Hello ES6!) // 键是函数m.get(hello) // Hello ES6!– Map.prototype.has(key)
has方法返回一个布尔值表示某个键是否在当前 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– Map.prototype.delete(key)
delete()方法删除某个键返回true。如果删除失败返回false。
const m new Map();
m.set(undefined, nah);
m.has(undefined) // truem.delete(undefined)
m.has(undefined) // false– Map.prototype.clear()
clear()方法清除所有成员没有返回值。
let map new Map();
map.set(foo, true);
map.set(bar, false);map.size // 2
map.clear()
map.size // 03.3 遍历方法
Map 结构原生提供三个遍历器生成函数和一个遍历方法。
Map.prototype.keys()返回键名的遍历器。 Map.prototype.values()返回键值的遍历器。 Map.prototype.entries()返回所有成员的遍历器。 Map.prototype.forEach()遍历 Map 的所有成员。
需要特别注意的是Map 的遍历顺序就是插入顺序。
const map new Map([[F, no],[T, yes],
]);for (let key of map.keys()) {console.log(key);
}
// F
// Tfor (let value of map.values()) {console.log(value);
}
// no
// yesfor (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上面代码最后的那个例子表示 Map 结构的默认遍历器接口Symbol.iterator属性就是entries方法。
map[Symbol.iterator] map.entries
// true———— Map 结构转为数组结构比较快速的方法是使用扩展运算符...。
const map new Map([[1, one],[2, two],[3, three],
]);[...map.keys()]
// [1, 2, 3][...map.values()]
// [one, two, three][...map.entries()]
// [[1,one], [2, two], [3, three]][...map]
// [[1,one], [2, two], [3, three]]☆ 结合数组实现map的过滤
结合数组的map方法、filter方法可以实现 Map 的遍历和过滤Map 本身没有map和filter方法。
const map0 new Map().set(1, a).set(2, b).set(3, c);const map1 new Map([...map0].filter(([k, v]) k 3) // map-array-map
);
// 产生 Map 结构 {1 a, 2 b}const map2 new Map([...map0].map(([k, v]) [k * 2, _ v]));
// 产生 Map 结构 {2 _a, 4 _b, 6 _c}————
– forEach()
此外Map 还有一个forEach方法与数组的forEach方法类似也可以实现遍历。
map.forEach(function(value, key, map) {console.log(Key: %s, Value: %s, key, value);
});forEach方法还可以接受第二个参数用来绑定this。
const reporter {report: function(key, value) {console.log(Key: %s, Value: %s, key, value);}
};map.forEach(function(value, key, map) {this.report(key, value);
}, reporter);上面代码中forEach方法的回调函数的this就指向reporter。 3.4 与其他数据结构的相互转换
– Map转为数组
前面已经提过Map 转为数组最方便的方法就是使用扩展运算符…。
const myMap new Map().set(true, 7).set({foo: 3}, [abc]);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ abc ] ] ]– 数组转为Map
将数组传入 Map 构造函数就可以转为 Map。
new Map([[true, 7],[{foo: 3}, [abc]]
])
// Map {
// true 7,
// Object {foo: 3} [abc]
// }– Map 转为对象
如果所有 Map 的键都是字符串它可以无损地转为对象。
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 }如果有非字符串的键名那么这个键名会被转成字符串再作为对象的键名。
– 对象转为 Map
对象转为 Map 可以通过Object.entries()。
let obj {a:1, b:2};
let map new Map(Object.entries(obj));此外也可以自己实现一个转换函数。
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}– Map 转为 JSON
Map 转为 JSON 要区分两种情况。一种情况是Map 的键名都是字符串这时可以选择转为对象 JSON。
function strMapToObj(strMap) {let obj Object.create(null);for (let [k,v] of strMap) {obj[k] v;}return obj;
}function strMapToJson(strMap) {return JSON.stringify(strMapToObj(strMap)); // map-Object-JSON
}let myMap new Map().set(yes, true).set(no, false);
strMapToJson(myMap)
// {yes:true,no:false}另一种情况是Map 的键名有非字符串这时可以选择转为数组 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]]]– JSON 转为 Map
JSON 转为 Map正常情况下所有键名都是字符串。
function objToStrMap(obj) {let strMap new Map();for (let k of Object.keys(obj)) {strMap.set(k, obj[k]);}return strMap;
}function jsonToStrMap(jsonStr) { // JSON-Object-Mapreturn objToStrMap(JSON.parse(jsonStr));
}jsonToStrMap({yes: true, no: false})
// Map {yes true, no false}但是有一种特殊情况整个 JSON 就是一个数组且每个数组成员本身又是一个有两个成员的数组。这时它可以一一对应地转为 Map。这往往是 Map 转为数组 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. WeakRef。。。 6. FinalizationRegistry : ES2021引入清理器注册表功能 END