Set
- 是一种新的数据结构 Set
- 类似于数组,但是成员的值都是唯一的,没有重复的值。
- Set 本身是一个构造函数,用来生成 Set 数据结构
// 通过new关键字创建一个Set结构
let s = new Set();
console.log(s);
// 添加数据
// 通过add方法
s.add(10);
console.log(s);
// 也可以
[1, 2, 3, 4, 1, 2, 3, 4].forEach(item => s.add(item));
// Set结构不允许添加重复的值
console.log(s);
// 在初始化时接受一个数组作为参数来创建Set
let s2 = new Set([1, 2, 3, 4, 5, 2, 3, 1, 4, 5, 5, 5]);
console.log(s2);
Set 去重判断
let s = new Set([5, "5"]);
console.log(s); // {5, '5'}
let a = NaN;
let b = NaN;
console.log(a === b);
s.add(a);
s.add(b);
console.log(s);
s.add({});
s.add({});
console.log(s);
- 向 Set 加入值时,不会发生类型转换,所以 5 和’5’是两个不同的值
- Set 内部判断两个值是否不同,使用的算法类似于全等运算符
- 主要区别在于,Set 加入值时认为 NaN 等于自身,但是全等运算符认为 NaN 不等于自身
- 再向 Set 添加对象时,总是不等的。
set 实例的属性和方法
-
属性
- size:返回的是 Set 实例的成员总数
-
方法:分为两类
- 操作方法
- 遍历方法
-
操作方法
- add(value):添加某个值
- 返回值:Set 结构本身
- delete(value):删除某个值
- 返回值:返回一个布尔值,表示删除是否成功
- has(value):查询该值是否为 Set 成员
- 返回值:一个布尔值
- clear:清除所有成员
- 返回值:空
- add(value):添加某个值
let s = new Set([1, 2, 3, 4]);
let flag = s.delete(1);
console.log(flag); // true 删除成功
console.log(s);
let flag2 = s.has(4);
console.log(flag2); // true
let flag3 = s.has(1);
console.log(flag3); // false
s.clear();
console.log(s); // Set(0) {size: 0}
- 遍历方法
- keys
- 语法:set.keys()
- 返回值:返回键名的遍历器
- values
- 语法:set.values()
- 返回值:返回键值的遍历器
- 注意:有 Set 结构没有键名,只有键值(或者说键名和键值是听一个值),所以 keys 和 values 方法的行为完全一致。
- entries
- 语法:set.entries()
- 返回值:返回键值对的遍历器
- keys
let s = new Set([10, 20, 30, 40, 50]);
let sKeys = s.keys();
console.log(sKeys);
for (let item of sKeys) {
console.log(item);
}
let sValues = s.values();
for (let item of sValues) {
console.log(item);
}
let sEntries = s.entries();
for (let item of sEntries) {
// 由于set结构中键名与键值相同,
// entries方法返回的遍历器同时包含键名和键值,所以输出时是一个数组,他的两个成员,值相等
console.log(item);
}
- forEach
- set 结构实例和数组一样,拥有 forEach 方法,用于对每个成员执行某种操作,没有返回值
s.forEach((item, key, arr) => {
console.log(item, key, arr);
// 10 10 Set(5) {10, 20, 30, 40, 50}
// 20 20 Set(5) {10, 20, 30, 40, 50}
// ...
});
set 的应用
- 数组去重
利用扩展运算符和 set 结构,对数组中的数据进行去重;
let arr = [3, 4, 5, 6, 1, 2, 4, 2, 4, 6, 2, 7];
let newArr = [...new Set(arr)];
// ...扩展运算符之所以可以将Set结构展开,原因在于,扩展运算符内部使用的是for of循环,所以可以用于set结构
console.log(newArr);
- 数组方法间接用于 Set
// 间接使用数组方法
// let set = new Set([1,2,3]);
// set = new Set([...set].map(item=> item*2));
// console.log(set);
// // filter
// set = new Set([...set].filter(item => item % 2))
// console.log(set);
// 并集
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 合并
let ab = new Set([...a, ...b]);
console.log(ab);
// 交集
let intersect = new Set([...a].filter(item => b.has(item)));
console.log(intersect);
// 差集
// a相对于b的差集
let difference = new Set([...a].filter(item => !b.has(item)));
let differenceB = new Set([...b].filter(item => !a.has(item)));
console.log(difference);
console.log(differenceB);
- Array.from 方法
- 语法:Array.from(你要转换为数组的数据,fn)
- 返回值:一个数组
- 作用:将一个类数组对象或可遍历对象转换为一个真正的数组
- 什么叫做类数组对象?
- 最基本的要求就是具有 length 属性的对象
- 什么叫做类数组对象?
// let arrayLike = {
// 0:"tom",
// 1:"65",
// 2:"男",
// 3:["zd","jh","rb"],
// length:4,
// }
// console.log(arrayLike);
// let arr = Array.from(arrayLike);
// console.log(arr);
// delete arrayLike.length;
// let arr2 = Array.from(arrayLike);
// console.log(arr2); // []
// 如果对象的属性名不是数字类型
let arrayLike = {
name: "tom",
age: "65",
gender: "男",
friends: ["zd","jh","rb"],
length: 4,
};
let arr = Array.from(arrayLike);
console.log(arr); // [undefined, undefined, undefined, undefined]
// 得到长度为4,元素均为undefined的数组
由此可见,要将一个类数组对象转换为一个真正的数组,必须具备以下条件:
- 该类数组对象必须具有 length 属性,用于指定数组的长度。如果没有 length 属性,那么转换后的数组是一个空数组
- 该类数组对象的属性名必须为数字类型或字符串类型的数字
// let arrayLike = {
// 0:"tom",
// 1:"65",
// 2:"男",
// 3:["zd","jh","rb"],
// length:4,
// }
// console.log(arrayLike);
// let arr = Array.from(arrayLike);
// console.log(arr);
// delete arrayLike.length;
// let arr2 = Array.from(arrayLike);
// console.log(arr2); // []
// 如果对象的属性名不是数字类型
// let arrayLike = {
// name:"tom",
// age:"65",
// gender:"男",
// friends:["zd","jh","rb"],
// length:4,
// }
// let arr = Array.from(arrayLike)
// console.log(arr);// [undefined, undefined, undefined, undefined]
// 得到长度为4,元素均为undefined的数组
// let arrayLike = {
// 0:"tom",
// 1:"65",
// 2:"男",
// 3:["zd","jh","rb"],
// 10:20,
// length:11,
// }
// let arr = Array.from(arrayLike);
// console.log(arr);
let lis = Array.from(document.querySelectorAll("li"));
console.log(lis);
// let arr = Array.from(lis)
// console.log(arr);
- 第二参数
- 类似于数组的 map 方法,用来对每个元素进行处理,将处理后的值放入返回的数组
let arr = [12, 45, 56, 76, 34, 23, 78];
let set = new Set(arr);
let arr2 = Array.from(set, (a, b, c) => {
console.log(a, b, c);
return a;
});
console.log(arr2);
// 字符串转数组
let str = new String("hello kitty");
console.log(Array.from(str));
console.log(str);
// 返回一个一摸一样的数组
console.log(Array.from([1, 2, 3, 4, 5, 6, 7, 8]));
Map
- 是一种新的数据结构
- 传统 js 中的对象,是键值对的集合(hash 结构),但是只能使用字符串作为键名
- Map 它类似于对象,也是键值对的集合,但是键名不限于字符串,可以是各种类型的值(包括对象)都可以作为键名
// 传统对象
const data = {};
const ele = document.querySelector("div");
let o = { name: "zd" };
data[ele] = "myDiv";
data[o] = "myO";
console.log(data);
console.log(ele.toString());
console.log(o.toString());
console.log(data[ele]);
console.log(data["[object HTMLDivElement]"]);
// 原意是想要使用一个DOM节点作为data对象的键名,但是由于对象只能接受字符串作为键名,所以ele被自动转为[object HTMLDivElement]
// Map对象
let m = new Map();
let obj = { name: "zd" };
// 向Map结构中添加数据
m.set(obj, "zd");
m.set(ele, "节点");
console.log(m);
// 获取Map中的数据
console.log(m.get(obj)); // "zd"
// 因为obj对象和{}对象不是同一个对象
console.log(m.get({})); // undefined
console.log(m.get(ele)); // 节点
console.log(m.get(document.querySelector("div"))); // 节点
Map 实例方法和属性
-
属性:
size:返回 Map 结构的成员总数 -
方法:
-
set(key,value):向 Map 结构中添加数据
- 参数:key:键名:value:键值
- 返回值:返回添加数据过后的 Map
let m = new Map();
m.set("name", "xx");
m.set(999, "笑笑");
m.set(undefined, "yanhao");
// 当发生重复时,与对象一致 新增会变为修改
m.set(undefined, "yaohao2");
console.log(m);
let res = m.set(null, "qx");
console.log(res);
console.log(m);
// 因为set方法返回的是当前的Map对象
// 所以可以使用链式写法添加
m.set({}, "hello")
.set([], "world")
.set(function () {}, "我是一个函数");
console.log(m);
// {'name' => 'xx', 999 => '笑笑', undefined => 'yaohao2', null => 'qx', {…} => 'hello', …}
let s = new Set([1, 2, 3]);
s.add(4).add(5).add(6).add(7);
console.log(s);
// 可以向Map传递一个数组作为参数来生成Map结构
// 但该数组的成员必须是一个个表示键值对的数组
// 键值对数组
let arr = [
["name", "xx"],
["age", 18],
];
let m2 = new Map(arr);
console.log(m2);
// Map接受数组作为参数,实际上执行的算法如下
let m3 = new Map();
arr.forEach(function ([key, value]) {
m3.set(key, value);
});
console.log(m3);
// 接受一个Set来生成Map
let set = new Set([
["name", "笑笑"],
["age", 18],
]);
console.log(set);
let m4 = new Map(set);
console.log(m4);
let m5 = new Map(m4);
console.log(m5);
console.log(m5 === m4);
- get(key):用于获取 Map 对象中对应 key 的值
- 参数:key 表示需要查找键值的键名
- 返回值:对应键值或 undefined(没找到的时候)
let m = new Map();
function foo() {
console.log(123);
}
m.set(foo, "是一个函数");
m.set(null, "qx");
m.set(undefined, "xx");
m.set({}, 123);
console.log(m);
console.log(m.get(foo)); // "是一个函数"
console.log(m.get(null)); // qx
console.log(m.get(undefined)); // xx
console.log(m.get({})); // undefined
- has(key):用于检测某个键在当前 Map 对象中是否存在
- 参数:key 表示要检测的键
- 返回值:是一个布尔值,true 表示存在反之不存在
console.log(m.has(undefined)); // true
console.log(m.has(function foo() {})); // false
console.log(m.has(foo)); // true
console.log(m.has({})); // false
- delete(key):删除某个键
- 参数:表示需要删除的键
- 返回值:一个布尔值。用于表示删除是否成功
console.log(m.delete(foo)); // 都为true
console.log(m.delete(null));
console.log(m.delete(undefined));
console.log(m.delete("qx")); // false
- clear:清除所有成员
- 参数:没有
- 返回值:没有
m.clear();
console.log(m); // {size: 0}
-
遍历方法
三个遍历器生成函数,一个遍历方法 -
keys:返回键名的遍历器
let m = new Map([
["name", "xx"],
["age", 3],
]);
for (let item of m.keys()) {
console.log(item); // name age
}
- values:返回键值的遍历器
for (let item of m.values()) {
console.log(item); // xx 3
}
- entries:返回所有成员的遍历器
for (let item of m.entries()) {
console.log(item);
// ['name', 'xx'] ['age', 3]
}
// 也可以
for (let [key, value] of m.entries()) {
console.log(key, value);
// name xx
// age 3
}
- forEach:遍历Map的所有成员
m.forEach(function(item,index,arr){
console.log(item,index,arr);
console.log(this);
// yh name Map(2) {'name' => 'xx', 'age' => 3}
// 3 'age' Map(2) {'name' => 'xx', 'age' => 3}
})
// Map的遍历顺序就是插入顺序
// 通过第二参数,来绑定当前方法中的执行函数指向
let arr = [1,2,3,4,5];
arr.forEach(function(item){
console.log(item);
console.log(this);
},m)
// 箭头函数无效
let res = arr.filter((item)=>{
console.log(this);
return item
},arr)
与其他数据结构的互相转换
// 转数组
// 直接使用扩展运算符
let m = new Map([
["name", "xx"],
["age", 18],
]);
let arr = [...m];
console.log(arr); // [Array(2), Array(2)]
// 数组转Map
// 将一个键值对数组传入Map构造函数,就可以转为Map
let m2 = new Map([
["name", "xx"],
["age", 18],
]);
console.log(m2); // {'name' => 'xx', 'age' => 18}
// Map转为对象
// 如果所有Map的键都是字符串,他可以无损的转为对象
// 如果有非字符串的键名,那么这个键名就会被转为字符串,在作为对象的键名,所以如果有多个对象键名则转为普通对象后,只会留下最后一个。
let obj = {};
for(let [k,v] of m){
obj[k] = v;
}
console.log(obj);
let yh = {
name:"笑笑"
}
let m3 = new Map([
[undefined,"xx"],
[null,"qx"],
[{},"jh"],
[yh,"yanhao"]
])
let obj2 = {};
for(let [k,v] of m3){
obj2[k] = v;
}
console.log(obj2); // {undefined: 'yh', null: 'qx', [object Object]: 'yanhao'}
// 对象转为Map
// 利用Object.entries()方法,将对象转为一个键值对数组。
// 将这个转换好的数组,作为Map结构初始化时的参数。
let obj3 = {
name:"xz",
age:18,
gender:"男"
}
let map = new Map(Object.entries(obj3));
console.log(map);
Iterator和Generator 迭代器和生成器
Iterator 迭代器
迭代器就是一种机制,是一种帮助我们对某种数据结构进行遍历的对象
在js中迭代器是一个对象,这个对象符合迭代器协议:定义产生一系列值的标准方式。
在js中就是一个next方法。而这个方法有如下要求
- 无参数或者一个有参数的函数
- 返回一个应当有以下两个属性的对象
done(boolean):表示迭代是否完成
true:表示迭代已完成
false:表示迭代没有完成
value
迭代器的返回值,done为true返回undefined(可以省略)
{
next(){
return {
done:
value:
}
}
}