Object.assign() 是一种浅拷贝

  1. let target = {}
  2. let source = {
  3. a: 1,
  4. b: 2
  5. }
  6. Object.assign(target, source)
  7. console.log('target', target)

image.png

对象的结构复杂化后

  1. let target = {
  2. a: 1,
  3. b: {
  4. f: 5,
  5. g: 6
  6. }
  7. }
  8. let source = {
  9. a: 1,
  10. b: {
  11. c: {
  12. d: 2,
  13. },
  14. g: 6
  15. }
  16. }
  17. Object.assign(target, source)
  18. console.log('target', target)

image.png
返回的数据中可以看出, source 对象新增数据到 target 中没有问题,但是深层的属性,容易被消除,比如target 对象 中的 b 属性下的 f 属性 在 拷贝后消失了。这说明 Object.assign() 存在一定的缺陷,或可以称之为 浅拷贝

Object.assign() 可以轻易的将 基本的数据类型 拷贝,但是对于 对象这样的引用数据类型则是直接将 引用地址赋值过去,这样就会导致 目标对象 数据的 破坏

浅拷贝与深拷贝的区别

浅拷贝对于 引用类型 是仅仅赋值其内存地址,其中一个对象值发生改变,拷贝的目标对象也会相应的改变,但是深拷贝,就不会产生影响

  1. let person = {
  2. name: '潜龙',
  3. age: 34
  4. }
  5. let person2 = person
  6. console.log('person',person)
  7. console.log('person2', person2)
  8. person.age = 18
  9. console.log('person2', person2)

image.png

这里的 person2 就是 浅拷贝的 person对象的内容, 所以当 person 对象的 age属性发生改变的时候,person2 中的 age 属性也会一起改变

如何实现 深拷贝

JSON.parse()&JSON.stringify()

JSON 的本质 就是一个字符串

JSON.parse()
将 json 格式的字符串 转化成 对象

JSON.stringify()
将 对象 转化成 json 字符串

let person = {
  name: '潜龙',
  age: 34
}

let str = JSON.stringify(person)
console.log('str', str);

let obj = JSON.parse(str);
console.log('obj', obj)

image.png

从这段代码可以看出, str 所表示的 数据只是一个字符串,是无法展开的

实现深拷贝

let obj1 = {
  name: '潜龙',
  age: 24
}

let str = JSON.stringify(obj1)
let obj2 = JSON.parse(str);
console.log('obj2',obj2)

obj1.age = 18
console.log('obj1', obj1)
console.log('obj2', obj2)

image.png
从这段代码可以看出,obj1 与 obj2 之间 属性值的改变是互不干扰的,达到了 深拷贝的目标

typeof

typeof 方法 判断数据类型的时候,存在一定的缺陷,typeof 可以用来判断基本数据类型,但是判断 引用数据类型的时候就会统一返回 object, 这样我们就没办法区分 数组和 对象了

let num = 1
let str = '1'
let flag = true
let obj = {}
let arr = []

console.log(typeof num);
console.log(typeof str);
console.log(typeof flag);
console.log(typeof obj)
console.log(typeof arr);

image.png

数组与对象类型区分检查方案

针对 typeof 无法区分 object 与 array 之间的区别
这里主要使用 Object.prototype.toString 方法进行区分

console.log('对象',Object.prototype.toString.call({}))
console.log('数组', Object.prototype.toString.call([]))

image.png
从这里我们可以看出 最后一个单词 帮我们区分出了 引用数据 的 确切类型, 为了方便判断我们可以使用 Stirng 的 分割 方法 slice()

let checkType = data => {
  let string = Object.prototype.toString.call(data)
  const type = string.slice(8, -1);
  return type
};

console.log('对象' ,checkType({}))
console.log('数组', checkType([]))

image.png

定义深拷贝方法

这里判断 变量的 类型到底是 数组还是 对象的原因是 方便创建 对象的初始值的数据类型

// 引用类型数据下,判断是数组还是对象
let checkType = data => {
  let string = Object.prototype.toString.call(data)
  const type = string.slice(8, -1);
  return type
};

// 深拷贝方法
let deepClone = target => {
  let targetType = checkType(target);
  let result
  // 这里只处理 引用类型, 如果是基本类型 则直接返回
  if (targetType === 'Object') {
    result = {};
  } else if (targetType === 'Array') {
    result = [];
  } else {
    return target;
  }

  for (let key in target) {
    let value = target[key]
    let valueType = checkType(value)
    if (valueType === 'Object' || valueType === 'Array') {
      // 如果数据仍然是 引用类型 则 进行递归处理
      result[key] = deepClone(value)
    } else {
      // 如果 value 是基本类型 则结束递归,直接赋值
      result[key] = value
    }
  }
  return result
};

测试数组

let arr1 = [1, 2, {age: 24}];
console.log('arr1', arr1)

let arr2 = deepClone(arr1);
console.log('arr2', arr2);

arr1[2].age = 18;
console.log('arr2', arr2)

image.png

当我们改变 arr1 的 值以后,arr2 没有受到影响表示,深拷贝成功

测试对象

let obj1 = {
  name: '潜龙',
  hobby: ['coding', 'eating']
}

let obj2 = deepClone(obj1);

obj1.hobby[0] = 'sleeping';
console.log('obj1', obj1);
console.log('obj2', obj2);

image.png