浅拷贝和深拷贝浅析

705 阅读3分钟

js数据类型

  1. 基本类型
    字符串(String)   数字(Number)    布尔(Boolean)
    空(Null)       未定义(Undefined)   记号(Symbol)
    大整数(BigInt)
  2. 引用数据类型
    对象(Object)     数组(Array)     函数(Function)
    正则(RegExp)   日期(Date)等

检查数据类型

  1. typeof
    用于判断数据类型,返回值一共有8个字符串,分别为:string,number,boolean,object,undefined,symbol,function,bigint
console.log(typeof 1)                          		//number
console.log(typeof true)                      		//boolean
console.log(typeof null)                      		//object
console.log(typeof undefined)            		//undefined
console.log(typeof Symbol('f'))           		//symbol
console.log(typeof {})                         		//object
console.log(typeof [])                          	//object
console.log(typeof function(){})           		//function
console.log(typeof 1n)                        		//bigint
console.log(typeof new RegExp("\\w+"))	                //object
console.log(typeof new Date())			        //object

可以看到,typeof无法区分null,数组、对象、正则和日期。
借助==比较的特点可以筛选出null

console.log(null==undefined)  //   true
console.log(null==null)       //   true
console.log(null==[])         //   false
console.log(null=={})         //   false

null除了和null、undefined进行==的结果为true以外,和其他任何类型==都为false,可以区别出null
2. instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
用法: obj instanceof Fn
obj._proto_._proto_...=>Fn.prototype, 沿着obj的原型链查找是否存在对象Fn.prototype,如果存在则返回true,如果找到obj原型链的终点Object.prototype都没有找到Fn.prototype,则返回false
通过instanceof可以对数组、对象、RegExp、Date进行区分:

console.log({} instanceof Object)  			//   true
console.log([] instanceof Array)   			//   true
console.log(new RegExp("\\w+") instanceof RegExp)	//   true
console.log(new Date() instanceof Date)			//   true
  1. Object.prototype.toString.call()
    Object.prototype.toString() 方法,会返回一个形如 "[object xxx]" 的字符串。但是不少对象的toString都进行了重写,比如数组:
console.log([1].toString())     //   1

数组重写了toString方法,直接调用数组对象的toString方法,实际调用到的是重写后的方法,并不是Object.prototype中的toString方法。 Object.prototype.toString.call()能对每一种数据类型进行精准的调用toString实现返回:

console.log(Object.prototype.toString.call('demo'))                   //[object String]
console.log(Object.prototype.toString.call(1))                        //[object Number]
console.log(Object.prototype.toString.call( true))                    //[object Boolean]
console.log(Object.prototype.toString.call( null))                    //[object Null]
console.log(Object.prototype.toString.call( undefined))               //[object Undefined]
console.log(Object.prototype.toString.call( Symbol('f')))          //[object Symbol]
console.log(Object.prototype.toString.call( {}))                      //[object Object]
console.log(Object.prototype.toString.call( []))                      //[object Array]
console.log(Object.prototype.toString.call( function(){}))            //[object Function]
console.log(Object.prototype.toString.call( 1n))                      //[object BigInt]
console.log(Object.prototype.toString.call(new RegExp("\\w+"))) //   [object RegExp]
console.log(Object.prototype.toString.call(new Date()))                  //   [object Date]

浅拷贝和深拷贝

对于基本数据类型的数据,深拷贝和浅拷贝的结果一样,拷贝后的数据与原始数据没有关系。深拷贝与浅拷贝只针对引用数据类型。
1.浅拷贝
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。最简单的浅拷贝就直接使用=赋值即可,浅拷贝以后的数据和原始数据指向同一个对象,对对象数据的修改会彼此影响
2. 深拷贝
深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

拷贝实现

  • 实现拷贝的内置函数
  1. Object.assign()
let objSrc = {
    a: {
        b: 1
    }
}
let objDest = Object.assign({}, objSrc);

Object.assign只能处理深度只有一层的对象,如果要复制的对象只有一层的话可以考虑使用它。
2. JSON.stringify

let objSrc = {
    a: {
        b: 1
    }
}
let objDest = JSON.parse(JSON.stringify(objSrc))

JSON.stringify能正确处理的对象只有 Number, String, Boolean, Array这些能够转成JSON格式的对象才可以这样用,像function、RegExp没办法转成JSON则无法使用

  • 手写深拷贝
function deepClone(obj) {
    if (typeof obj !== 'object' || obj==null) {
        return obj
    }
    if (obj instanceof RegExp) {
        return new RegExp(obj.source, obj.flags)
    }
    if (obj instanceof Date) {
        return new Date(obj.getTime())
    }
    let result
    if (Array.isArray(obj)) {
        result = []
    } else {
        result = {}
    }
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            result[key] = deepClone(obj[key])
        }
    }
    return result
}