深浅拷贝傻傻分不清?

1,154 阅读3分钟

本文正在参加「金石计划 . 瓜分6万现金大奖」 

浅拷贝

浅拷贝会创建一个新的对象,浅拷贝得到的对象有着原对象属性的一份精确拷贝,如果属性是基本数据类型,拷贝的就是基本数据类型的值,如果属性是引用类型,拷贝的就是内存地址,无论我们修改拷贝之后的对象或者是原对象共有的属性时,另一个对象也会受到改变

image.png

简而言之浅拷贝就是指拷贝一层,拷贝第一层的基本数据类型的值,以及第一层的引用类型地址。

如何实现浅拷贝

1、Object.assign()

对象身上的一个方法,可以接受无限个参数,将其中第一个参数作为基础,后面的所有参数,只要第一个参数存在这个键名,就将值的地址指向修改为后面出现的地址,如果不存在,则添加一个新的属性,地址为后出现的地址,以此往复。

// 简易版Object.assign()
Object.prototype.my_assign = function(obj,...args) {
  if(obj == null) throw new TypeError('Cannot convert undefined or null to object');  // (目标对象不能为空,我们可以直接设置{}传递进去,但必须设置值)。
  const arr = [...args]
  for(let i = 0; i < arr.length; i++) {
    for(let key in arr[i]) {
      arr[i].hasOwnProperty(key) && (obj[key] = arr[i][key])
    }
  }
  return obj
}
// Object.assign()实现浅拷贝:
let a = {
  name: '寒月十九',
    age: 20,
    like: {
        n1: 'coding',
        n2: 'reading'
    }
}
let b = Object.assign({},a )

image.png

2、解构

// Object.assign()实现浅拷贝:
let a = {
  name: '寒月十九',
    age: 20,
    like: {
        n1: 'coding',
        n2: 'reading'
    }
}
let b = {...a}

image.png

3、Array.prototype.concat()

image.png 4、Array.prototype.slice()

image.png 后两个方法就不过多展示了,聊完浅拷贝,我们来看看深拷贝。

深拷贝

深拷贝会拷贝所有属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。

image.png

如何实现深拷贝

JSON.parse() && JSON.stringify()

JSON.parse()方法用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象。

JSON.stringify()方法将一个 JavaScript 对象或值转换为 JSON 字符串

let a = {
  name: '寒月十九',
    age: 20,
    like: {
        n1: 'coding',
        n2: 'reading'
    }
}

let b = JSON.parse(JSON.stringify(a));
b.name = '十九';
b.like.n1 = 'basketball';
console.log(a);

image.png

我们可以看到当我们修改深拷贝得到的对象b中的两个属性,一个属性为基本数据类型,一个为引用类类型,但是原对象a不受影响,但是借助JSON对象有几个缺点,需要我们注意下。

1、会忽略undefined、Symbol、函数(不能序列化)

let obj = {
  name: '寒月十九',
  a: undefined,
  b: Symbol('harvey'),
  c: function() {}
}
let obj1 = JSON.parse(JSON.stringify(obj));
console.log(obj1);

image.png

2、不能拷贝循环引用的对象

image.png

3、拷贝new Date()转换结果不正确

image.png

可以将其转为时间戳,就可以实现拷贝。

4、无法处理正则

image.png

5、对象中含有NaN,Infinity会变成null

image.png

手撕拷贝

手撕浅拷贝

// 浅拷贝的实现原理(仿造Object.assign())

function shallowCopy(obj) {
    if(typeof obj !== 'object') return 
    let newObj = obj instanceof Array ? [] : {}
    for(let key in obj){
        if(obj.hasOwnProperty(key)) {  // 判断是不是对象的显示属性
            newObj[key] = obj[key]
        }
    }
    return newObj
}

代码测试:

image.png

手撕深拷贝

function deepCopy(obj) {
    if(typeof obj !== 'object') return obj// 不是引用类型,不需要拷贝
    let newobj = obj instanceof Array ? [] : {}
    for(let key in obj) {
        if(obj.hasOwnProperty(key)){
            newobj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]
        }
    }
    return newobj
}

代码测试:

image.png