数据类型检测
数据类型检测共有四种方案
- typeof检测出所有的基本数据类型
(typeof null为"object" 因为typeof底层是基于二进制而null得开头前三个数为000,所有对象得前三个数都为000所以null就被检测成了"object")(number,string,boolean,undefined,symbol,bigint,object,function)
- instanceof
实例 instanceof 类如果这个类的原型对象在这个实例的原型链上就为true,实际底层调用了Function.prototype上的Symbol.hasInstance方法 类[Symbol.hasInstance] (实例)
- constructor
通过实例的constructor可以得到它的类,如果constructor被修改就不准确了
- Object.prototyep.toString.call(任何数据)
这种方法比较友好,最好用来检测对象
通过以上几种方式检测数据类型,typeof方法更适合检测基本数据类型和function,instanceof和constructor都有被修改的风险,并且instanceof得到的结果比较宽泛({}).toString.call(数据)方法可以检测所有数据类型得到 "[object 数据所属的类]"
终极方案
(function() {
let typeObj = {}
["[object Date]", "[object Math]", "[object Array]", "[object Object]", "[object Function]","[object Error]"].forEach(item => {
item.replace(/^\[object ([a-z]+)\]$/i, (_, type) => {
typeObj[item] = type.toLowerCase()
})
})
window.typeObj = typeObj
})()
function toType(data) {
//把他按基本数据类型和引用数据类型分配
let type = typeof data
if (type === "object") {
type = typeObj[{}.toString.call(data)]
}
return type
}
实现浅拷贝
浅拷贝方法要传入的值可能不仅仅是数组或者普通对象,要先根据toType检测他们的数据类型,如果是symbol或者bigint类型的就要Object(obj),如果是Date/Regexp类的实例的要new obj.constructor(obj),要是error的话就要new obj.constructor(obj.message) 要是function的话就要返回一个方法,方法中调用obj方法,并让它的this指向为返回的函数调用时的this,最后如果obj是数组或对象,拿到它所有key(包括symbol属性)遍历浅拷贝
function shallowClone(obj){
let type=toType(obj)
let cons=obj.constructor
if(obj==undefined){
return obj
}
if(/^(symbol|bigint)$/.test(type))return Object(obj)
if(/^(date|regexp)$/.test(type))return new cons(obj)
if(/^(error)$/.test(type))return Object(obj.message)
if(/^(function)$/.test(type)){
return function(...args){
obj.call(this,...args)
}
}
if(/^(obj|array)$/.test(type)){
let result=new constructor
let keys=[
...Object.keys(obj),
...Object.getOwnPropertySymbols
]
keys.forEach(key=>{
result[key]=obj[key]
})
return result
}
retrun obj
}
深拷贝
浅拷贝完成后实现深拷贝就要更容易些,实际上之所以需要深拷贝是因为数组或普通对象存在深层级的嵌套,所以如果传进来的值不是array或者object类型的可以直接通过shallowClone完成拷贝,剩下Array和Object主要让他们的属性值递归调用deepclone方法 这里有一个小坑,当obj.xxx=obj的时候就会陷入递归的死循环,这时最好设置一个map它的ky存放要拷贝的对象或数组,值是拷贝后的对象或数组,每次先看obj是否已经存在,如果存在直接返回拷贝好的对象,而不要再次拷贝
function deepclone(obj,store=new WeakMap()){
if(!(/^(obj|array)$/.test(type))){
return shallowClone(obj)
}
let hasRea=store.get(obj)
if(hasRes){
return hasRes
}
let result=new constructor
store.set(obj,result)
let keys=[
...Object.keys(obj),
...Object.getOwnPropertySymbols
]
keys.forEach(key=>{
result[key]=deepclone(obj[key],store)
})
return result
}