数据类型检测与浅拷贝深拷贝

140 阅读2分钟

数据类型检测

数据类型检测共有四种方案

  • 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
    }