本篇主要介绍其他常用的对象(正则表达式比较特殊,单独放一章),然后对不同数据类型的存储结构进行归纳。
Date
常用的一些方法:
- +new Date() // 获取当前时间戳
- Date.now() // 获取当前时间戳
- getTime() // 表示返回的毫秒数
- getFullYear() // 返回四位数年份
- getMonth() // 返回当前月份(0-11)
- getDate() // 返回当前天数
- getDay() // 返回当前星期几(0表示星期天,6表示星期六)
- getHours()
- getMinutes()
- getSeconds()
项目过程中经常会使用momentjs和dayjs两个库进行和时间相关的处理,需要涉及时间的项目可以参考两个库的Api
Map,WeakMap,Set,WeakSet
1.Set
本身是一个构造函数,这个数据结构类似于数组,但是成员的值都是唯一的
所以对于简单的数组去重以及字符串去重,可以直接使用Set就可以完成
let s = new Set();
[1,2,3,5,2,2].forEach(x=>s.add(x)) // [1,2,3,5]
[...new Set(array)] // 数组去重
[...new Set(string)].join("") // 字符串去重
Set函数可以接受一个数组,或者类似数组的对象作为参数
向 Set 加入值的时候,不会发生类型转换,所以5
和"5"
是两个不同的值。Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”,它类似于精确相等运算符(===
),主要的区别是向 Set 加入值时认为NaN
等于自身,而精确相等运算符认为NaN
不等于自身,两个对象总是不相等的
将Set结构转化为数组:Array.from(s);
扩展运算符(...
)内部使用for...of
循环
Set的实例属性:1.constructor:构造函数;2.size,Set实例的成员总数,类似于数组的length
Set的方法:
1.操作方法
add(value),添加某个值,返回函数本身
delete(value),删除某个值,返回函数本身
has(value),判断成员中是否包含value,返回boolean
clear(),清除所有成员,没返回值
2.遍历方法
keys();返回键名的遍历器
values();返回键值的遍历器
entries();返回键值对的遍历器
forEach();使用回调函数遍历每个成员
Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys
方法和values
方法的行为完全一致。
2.WeakSet
WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。
1.WeakSet 的成员只能是对象,而不能是其他类型的值
2.WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中
垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakSet 不可遍历。
1.操作方法
add(value),添加某个值,返回函数本身
delete(value),删除某个值,返回函数本身
has(value),判断成员中是否包含value,返回boolean
3.Map
Map类似于对象,本质上也是键值对的组合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
如果对同一个键多次赋值,后面的值将覆盖前面的值,如果读取一个未知的键,则返回undefined
只有对同一个对象的引用,Map 结构才将其视为同一个键。Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题
1.属性:
size,返回Map结构的成员总数;
set(key, value),设置键名key对应的键值value,覆盖更新;
get(key),获取key对应的value,如果找不到key,返回undefined;
has(key),返回boolean;
delete(key),删除某个键,返回是否删除成功的boolean;
clear(),清除所有成员,没有返回值
2.遍历方法
keys(),返回键名的遍历器
values(),返回键值的遍历器
entries(),返回所有成员的遍历器
forEach(),遍历Map的所有成员
对象转为 Map 可以通过Object.entries()
。
4.WeakMap
WeakMap
结构与Map
结构类似,也是用于生成键值对的集合。区别有两点:
1.WeakMap
只接受对象作为键名(null
除外),不接受其他类型的值作为键名。
2.WeakMap
的键名所指向的对象,不计入垃圾回收机制
如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap。一个典型应用场景是,在网页的 DOM 元素上添加数据,就可以使用WeakMap
结构。当该 DOM 元素被清除,其所对应的WeakMap
记录就会自动被移除。
WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。
基本数据类型和引用类型的区别
到现在为止,基本数据类型和引用类型就总结的差不多了,在最后,要对两种类型进行总结。
数据类型的保存
之前说过,计算机程序运行过程中需要通过变量对值进行保存,js的变量是保存在栈内存中,值与值之间是独立存在的,修改其中一个值并不会改变其他值。
基本类型的值会直接在栈内存中储存,而引用类型会分为两部分,创建一个新的引用类型时,会开辟一个堆内存空间,将值保存在堆空间中,同时在栈内存中会存放一个指向堆内存的指针(相当于这个堆内存的门牌号和钥匙)。
浅拷贝和深拷贝
2.赋值:
基本数据类型:
var a = 123; var b = a;
第一步:给a分配一个栈内存
第二步:给b分配一个栈内存,将a中的数据拷贝出来
因此,基本数据类型赋值拷贝之后两个变量互相不影响
引用类型:
js var a = [qqq]; var b = a
第一步:分配一个堆内存,存储数据,分配一个栈内存,存储a对应的数据的堆内存地址(堆1),a可以根据地址找到相应的数据
第二步:给b分配一个栈内存,存储对应的堆内存地址(还是堆1),b也可以根据地址找到相应的数据。
当a或者b改变时,改变的是堆内存里面的数据,栈内存里的地址没有改变,因此引用类型赋值拷贝之后两个变量是相互影响的,一个变量改变会导致另一个变量的改变。
3.引用类型拷贝
如果想要对引用类型进行拷贝,使拷贝之后的结果互不影响的话,需要在拷贝时新增一个堆内存,将原始堆内存中的数据复制进去,同时新分配的栈内存存储新的堆内存地址。
4.深拷贝的方式
1.利用json进行转化
var a = JSON.stringify(b);
var c = JSON.parse(a); //将b拷贝给c
将b转化为字符串赋值给a,然后将a再转化为json赋予c
弊端:只能转化只有基础类型的对象,包含引用类型嵌套时不能使用
2.利用数组中的slice和concat方法
var a = ["1","2","3"];
var b = a.slice(0);
b[1] = "4"; //a = ["1","2","3"],b = ["1","4","3"]
var c = a.concat();
c[0] = "5"; //a = ["1","2","3"],c = ["5","2","3"]
弊端:无法切断引用类型嵌套时的关联,slice和concat类似
var aa = ["1",["1","2"],"3"];
var bb = aa.slice(0);
bb[1][1] = "33"; //aa=["1",["1","33"],"3"],此时嵌套的数据会被修改
bb[1] = "33"; //aa=["1",["1","33"],"3"],bb=["1", "33", "3"],此时嵌套的数据不会被修改
真正深拷贝需要深层遍历实现,lodash有cloneDeep方法,可以快速实现深拷贝
function deepClone(obj){
let cloneObj = {};
if(obj===null) return obj;
if(obj instanceof Date) return new Date(obj);
if(obj instanceof RegExp) return new RegExp(obj);
if(typeof obj !=='object') return obj;
for(let i in obj){
if(obj.hasOwnproperty(i)){
cloneObj[i] = deepClone(obj[i])
}
}
return cloneObj
}