数据结构:存储数据的方式
Set是一个新增的数据结构,可以用来保存数据,类似于数组,但和数组最大的区别是 元素不能重复
应用场景
// 10,20,40,333
// * 创建Set结构
let set=new Set();
// * 添加元素
set.add(10)
set.add(20)
set.add(40)
set.add(333)
set.add(10);//重复的元素会被忽略
// * 添加对象的注意点
set.add({});//两个对象是有不同的内存地址
set.add({});//和上面的{}的地址是不同的
const obj={name:"wjy"};
set.add(obj);
set.add(obj); //*重复的内存地址也只会被添加一次
console.log(set);
// * 应用场景:①不想添加的元素重复 ② 给数组去重
const arr=[22,10,20,45,22,10,2];
// * 1.自己写的一个方法
const newArr=[];
for(const item of arr){
if(newArr.indexOf(item)==-1){
newArr.push(item)
}
}
// * 可以使用Set
const arrSet=new Set(arr);
// * 方法1 Array.from
const newArr2=Array.from(arrSet);//* Array.from():对一个类似数组或可迭代对象创建一个新的、浅拷贝的数组实例
console.log(newArr2);
// * 方法2:展开运算符
const newArr3=[...arrSet];
console.log(newArr3);
console.log(set.size);
/**
* * 常见的方法
* * 1. add(item):添加指定元素
* * 2. delete(item):删除指定元素
* * 3.has(item):判断指定元素是否在Set中,如果有则返回true,否则返回false
* * 4.clear():清空Set集合
*/
set.add("wjy");
console.log(set);
set.delete("wjy");
console.log(set);
console.log(set.has("wjy"));
set.forEach(item=>{
console.log(item);
})
for(const item of set){
console.log(item);
}
set.clear();
console.log(set);
和Set类似的另外一个数据结构称之为WeakSet,也是内部元素不能重复的数据结构。
强引用指的的是 指向的那条线是生效的。
弱引用指的是指向的那条线存在不存在,没有任何关系。
let obj={name:"why"};
let set=new Set();
set.add(obj);
console.log(set);
如果将obj设置为null,set存储的元素也还是会被引用。
obj=null;
console.log(set);
但如果是 WeakSet,因为WeakSet对元素的引用是弱引用,所以由weakSet指向那片地址其实没有用的,不算真正的引用,如果初略WeakSet本身实例指向那片地址,没有其他引用引用这个对象的话,这个对象会被GC回收掉。
const weakSet=new WeakSet();
let obj={name:"why"};
weakSet.add(obj);
obj=null;
当obj不再引用这片地址空间时,没有其他引用引用了,除了weakSet本身的弱引用(但是这个弱引用是没有用的),这片内存空间会被GC回收掉。
注意:WeakSet不能遍历
我们使用一个Stack Overflow上的答案:只能使用Person实例调用running,相对于Set比较方便,不需要手动是释放内存。
let weak=new WeakSet();
class Person{
constructor(){
weak.add(this);
}
running(){
if(!weak.has(this)) throw new Error("Type Error ,NOT Person")
console.log("running");
}
}
const p=new Person();
p.running.call({name:'wjy'});
另外一个新增的数据结构是Map,用于存储映射关系。
但是我们可能会想,在之前我们可以使用对象来存储映射关系,他们有什么区别呢?
let obj1={name:"wjy"};
let obj2={name:"hyz"};
let o={
[obj1]:'aaa',
[obj2]:'bbb'
}
//* 因为对象后面被直接转换为 字符串[object Object],所以当同样的key被重复赋值时,只会保留最新的值。
//* []:这个是计算属性
console.log(o);//{ '[object Object]': 'bbb' }
那么我们就可以使用Map:
// * Map允许对象作为key
let map=new Map();
let obj1={name:"wjy"};
let obj2={name:"hyz"};
map.set(obj1,"aaa");
map.set(obj2,"bbb")
map.set("name","wjy")
console.log(map);
// Map(3) {
// { name: 'wjy' } => 'aaa',
// { name: 'hyz' } => 'bbb',
// 'name' => 'wjy'
// }
let map2=new Map([[obj1,"aaa"],[obj2,"bbb"],["name","wjy"]]);//可以传入数组
console.log(map2);
// Map(3) {
// { name: 'wjy' } => 'aaa',
// { name: 'hyz' } => 'bbb',
// 'name' => 'wjy'
// }
Map常见的属性:
Map常用的方法:
/**
* * 常用属性
* * size:获取Map的元素个数
*/
console.log(map2.size);//3
/**
* * 常用方法
* * set:添加一个key和value,并将整个Map对象返回
* * get:根据key获取Map中的value
* * has:判断是否包含某个key,返回Boolean
* * delete:根据key,删除键值对,删除成功返回true,删除失败返回false
* * clear:清空
*/
console.log(map2.get(obj1));//aaa
console.log(map2.has("name"));//true
console.log(map2.delete("aa"));//false
map2.clear();
console.log(map2);//Map(0) {}
和Map类型相似的另外一个数据结构称之为WeakMap,也是以键值对的形式存在的。
那么和Map有什么区别呢?
Map是对key的强引用,WeakMap是对key的弱引用(这个引用是没有关系的)
let obj1={name:"obj1"};
const map=new Map();
map.set(obj1,"aaaa");
obj1=null
将obj1指向null,不指向那片内存空间,但是由于Map中对key的引用是强引用,所以并不会被回收掉的。
但如果是WeakMap结果就是不一样的了。
let obj1={name:"obj1"};
const map=new WeakMap();
map.set(obj1,"aaa");
obj1=null;
将obj1指向null,不再指向那片空间,由于在weakMap中是弱引用,所以那片空间是可以被GC回收的。
不能遍历
let obj1={name:"obj1"};
const map=new WeakMap();
map.set(obj1,"aaa");
/**
* * 常见的方法
* * set
* * get
* * delete
* * has
*/
console.log(map.get(obj1));//aaa
console.log(map.has(obj1));//true
console.log(map.delete(obj1));//true
let obj1={name:"wjy",age:20};
function obj1NameFn1(){
console.log("obj1NameFn1被执行了");
}
function obj1NameFn2(){
console.log("obj1NameFn2被执行了");
}
function obj1AgeFn1(){
console.log("obj1AgeFn1被执行了");
}
function obj1AgeFn2(){
console.log("obj1AgeFn2被执行了");
}
let obj2={
name:"kobe",
height:1.88,
address:"广州市"
}
function obj2NameFn1(){
console.log("obj2NameFn1被执行了");
}
function obj2NameFn2(){
console.log("obj2NameFn2被执行了");
}
// 1. 创建一个WeakMap
let weakMap=new WeakMap();
// 2.1 收集依赖数据结构:对obj1收集的数据结构
const obj1Map=new Map();
obj1Map.set("name",[obj1NameFn1,obj1NameFn2]);
obj1Map.set("age",[obj1AgeFn1,obj1AgeFn2]);
weakMap.set(obj1,obj1Map)
// 2.2.收集依赖数据结构
const obj2Map=new Map();
obj2Map.set("name",[obj2NameFn1,obj2NameFn2]);
weakMap.set(obj2,obj2Map)
// 3 如果obj1.name发生了改变
// Proxy/Object.defineProperty
obj1.name="james";
const targetMap=weakMap.get(obj1);
const fns=targetMap.get("name");
fns.forEach(item=>item())
在ES7之前,如果我们想判断一个数组中是否包含某个元素,需要通过indexOf获取结果,并且判断是否为-1
在ES7之后,我们可以通过includes来判断一个数组中是否包含一个指定的元素,根据情况,如果包含则返回true,否则返回false
语法:
Array.includes(item,startIndex)
const names=["wjy","hyz","tl"];
if(names.indexOf("abc")!==-1){ //阅读性差
console.log("包含");
}
// ES7 Array.includes():判断数组是否包含某个元素
// includes(item,startIndex); startIndex:从哪个位置开始判断
if(names.includes("wjy",0)){
console.log("包含");
}
如果数组中有个元素为NaN,
如果使用indexOf判断NaN是否存在,它会返回-1(找不到NaN这个元素)
如果使用includes判断NaN是否存在,它会返回true(找到了这个NaN元素)
const names=["wjy","hyz","tl",NaN];
if(names.indexOf(NaN)!==-1){ //* 即使数组中有NaN,但还是被判断为不存在
console.log("包含NaN");
}
if(names.includes(NaN)){
console.log("包含NaN");
}
在ES7之前,计算数字的乘方需要使用Math.pow方法来完成。
在ES7中,增加了**运算符,可以对数字来计算乘方。
// 计算3的3次方
// ES7之前
const result1=Math.pow(3,3);
// ES7后
const result2=3**3;
console.log(result1==result2);//true