Skip to content

JS 深拷贝

需要用到深拷贝的原因,引用类型被修改会导致其他数据被改动,而经常赋值操作希望操作别的数据不要影响自己

JS数据类型-引用类型

Object.assign

可以深拷贝对象的一层属性

...展开运算符

可以深拷贝对象的一层属性

JSON.stringify

可以拷贝对象深层属性,但是会丢失

  • undefined
  • symbol
  • function
js
let a = {
  age: undefined,
  sex: Symbol('male'),
  jobs: function() {},
  name: '11'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "11"}

嵌套对象属性会抛错

MessageChannel

MessageChannel-MDN

可以深拷贝嵌套引用和 undefined symbol ,但是不能拷贝 function

js
const obj = {
  a: undefined,
  sex: Symbol('male'),
  b: {
    c: 2
  }
}
obj.b.d = obj.b // 嵌套引用

// 注意该方法是异步的
function deepClone(obj) {
  return new Promise(resolve => {
    const { port1, port2 } = new MessageChannel()
    port2.onmessage = ev => resolve(ev.data)
    port1.postMessage(obj)
  })
}

// 可以处理 undefined symbol 和嵌套引用对象
const test = async () => {
  const clone = await deepClone(obj)
  console.log(clone)
}
test()

手写深拷贝-广度优先递归

手写深拷贝-深度优先递归

js
function deepClone(obj) {
  function isObject(o) {
    return (typeof o === 'object' || typeof o === 'function') && o !== null
  }

  if (!isObject(obj)) {
    throw new Error('非对象') // 非对象通过抛错跳过
  }

  // 数组类型
  let isArray = Array.isArray(obj)
  let newObj = isArray ? [...obj] : { ...obj }

  // TODO: Reflect.ownKeys
  Reflect.ownKeys(newObj).forEach(key => {
    // 递归执行自身
    newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
  })

  return newObj
}

let obj = {
  a: [1, 2, 3],
  b: {
    c: 2,
    d: 3
  }
}

let newObj = deepClone(obj)
newObj.b.c = 1
console.log(obj.b.c) // 2

递归深度判断对象属性是否全等

js
function isObjEqual(obj1, obj2) {
// 获取key
const obj1KeyList = Object.getOwnPropertyNames(obj1);
const obj2KeyList = Object.getOwnPropertyNames(obj2);
// key长度不符,对象不相等
if (obj1KeyList.length !== obj2KeyList.length) {
return false;
}
for (const key of obj1KeyList) {
const obj1Value = obj1[key];
const obj2Value = obj2[key];
if (typeof (obj1Value) === \'object\') {

// 递归

if (!isObjEqual(obj1Value, obj2Value)) {

return false;

}
} else if (obj1Value !== obj2Value) {

return false;
}
}
return true;}console.log(isObjEqual([],[]))

参考

lodash深拷贝-文档lodash深拷贝-github