深拷贝浅拷贝

99 阅读1分钟

一、浅拷贝

浅拷贝只能解决一层的拷贝,属性值为引用地址时则复制的是引用地址

1、浅拷贝:Object.assign()

合并对象,后面参数的属性会覆盖前面同名属性

var obj = {a:[1,2,3],b:'b'};
var test = Object.assign({},obj);obj.a.push("123");console.log(test);
//{a:[1,2,3,'123'],b: "b"}

注意:参数为数组时,会把数组当成对象来处理 Object.assign([1,2,3],[45]);//[4,5,3]

2、...解构赋值进行浅拷贝

var obj={a:'a',b:'b'}; var result = {...obj}; result.a;//a

3、数组浅拷贝

对于值都是普通数据类型的数组,可以使用数组的 slice 或者 concat 函数来进行深拷贝。 var a = [1,2,3]; var b = a.slice(0); var c = [].concat(a);console.log(c);

二、深拷贝

1、使用JSON序列化函数进行深拷贝

JSON.parse(JSON.stringify(obj))

  1. 会忽略值为undefined、Symbol、函数的键值对,不能序列化函数,不能解决循环引用的对象
  2. 数组中的空位和数组中的undefined会转换成null
  3. 值为new Data(),会转换成时间字符串

循环引用报错

let obj = {
  a: 1,
  b: {}  
}
obj.b.c = obj.b
let result = JSON.parse(JSON.stringify(obj))
console.log(result)

image.png

忽略undefined、symbol、函数

var objtest= {
a:undefined,
b:Symbol('sym'),
c:function(){},
d:"test"};
var result = JSON.parse(JSON.stringify(objtest));
console.log(result);
//{d:"test"}

解决深拷贝以上问题:

<script>
const isObject = obj => {
  return typeof obj === "object" && obj != null
}

const cloneDeep = (obj, hash = new WeakMap()) => {
  if (!isObject(obj)) {
    return obj
  }

  if (hash.has(obj)) { // 解决循环引用引发的问题
    return hash.get(obj)
  }

  const type = [Date, RegExp, Set, Map, WeakMap, WeakSet]
 
  if (type.includes(obj.constructor)) {
    return new obj.constructor(obj)
  }

  const allDesc = Object.getOwnPropertyDescriptors(obj) // 遍历传入参数所有键的特性

  //Object.getPrototypeOf(obj)新创建对象的原型对象
  //allDesc传入对象的自有可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)将为新创建的对象添加指定的属性值和对应的属性描述符。
  const cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc) // 继承原型
  hash.set(obj, cloneObj)

  //**Reflect.ownKeys(target)**是返回所有的属性key,包括不可枚举类型,不包括继承的属性
  for (let key of Reflect.ownKeys(obj)) {
    // Reflect.ownKeys(obj)可以拷贝不可枚举属性和Symbol类型
    // 注意:writable 为 false 的属性会赋值失败,因此 writable 为 false 的属性是浅拷贝
    cloneObj[key] = isObject(obj[key]) ? cloneDeep(obj[key], hash) : obj[key]
  }
  return cloneObj
}

// 测试
let obj = { 
  bigInt: BigInt(1111111111),
  set: new Set([2]),
  map: new Map([
    ["a", 33],
    ["b", 44]
  ]),
  num: 0,
  str: "",
  boolean: true,
  unf: undefined,
  nul: null,
  innerObj: {
    name: "我是一个对象",
    id: 1
  },
  arr: [0, 1, 2],
  func: function () {
    console.log("函数")
  },
  date: new Date(0),
  reg: new RegExp("/正则/ig"),
  [Symbol("1")]: 1,
  loopA:{a:'a'},
  innerObj:{a:'a',b:'b',c:'c',d:'d'}
}
obj.loopA.b = obj.loopA;

let cloneObj = cloneDeep(obj)
console.log("obj", obj)
console.log("cloneObj", cloneObj)
</script>