如何浅合并一下 JavaScript 对象(其实希望深拷贝)

由于业务需求,本人需要将两个对象进行合并。 bussiness

浅合并

对象层级较浅的情况,可以使用 ES6 ...语法,解构赋值,或Object.assign()快速合并;但对象里有子对象嵌套时,前两招就不好使了。要么使用第三方库,如 loadash 的 _.merge() 或 jQuery 的 $.extend() 方法,要么自己封装一个函数。

说聊聊几个做浅拷贝的方法。

... 拷贝对象属性

使用扩展运算符 {...obj} 克隆或者拷贝对象的属性:

const obj1 = { name: "Nic" }
const clonedObj = { ...obj1 }

console.log(clonedObj)  // { name: "Nic" }

... 合并对象

使用扩展运算符 {...obj} 合并两个对象,可以看到,扩展运算符内部的同名属性会被覆盖掉:

const obj1 = { a: 1 }
const obj2 = { a: 2, b: 3 }
const obj3 = { a: 3, b: 4, c: 5 }

const obj = { ...obj1, ...obj2, ...obj3 }

console.log(obj); // { a: 3, b: 4, c: 5 }

扩展运算符得到的只是浅拷贝。

clonedObj拷贝的是引用地址,当obj1.hobbies.gaming属性改变时,clonedObj.hobbies.gaming也随之改变:

const obj1 = {
  name: "Nic",
  hobbies: {
    gaming: "fall guys",
    reading: "novel"
  }
}
const clonedObj = { ...obj1 }
obj1.hobbies.gaming = "splatoon 2"

console.log(clonedObj.hobbies.gaming)  // splatoon 2

Object.assign()

接下来看看 Object.assign() 表现如何。
Object.assign() 方法将所有可枚举(*Object.propertyIsEnumerable() 返回 true*)和自有(*Object.hasOwnProperty() 返回 true*)属性从一个或多个源对象复制到目标对象,返回修改后的对象。

语法:Object.assign(target, source)

MDN Demo:

const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };

const returnedTarget = Object.assign(target, source);

console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }

console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }

遗憾的是,Object.assign() 也是浅拷贝。

const obj1 = {
  name: "Nic",
  hobbies: {
    gaming: "fall guys",
    reading: "novel"
  }
}
const clonedObj = { ...obj1 }
// obj1.hobbies.gaming = "splatoon 2"

// console.log(clonedObj.hobbies.gaming) 


const clonedObj2 = Object.assign(clonedObj, obj1)
obj1.hobbies.gaming = "splatoon 2"

console.log(clonedObj.hobbies.gaming) 

深合并

这么看来,EMCAScript 似乎并没有处理深拷贝的方法供我们使用。可我就不想用第三方库啊摔!那怎么办?选择先找一个别人写好的方法

以下记录一个利用递归合并对象的方法并封装于 vue 项目中的过程。

step 1: 方法./src/utils 下新建 mergeObj.js文件,添加如下代码

// 封装的一个 js 对象递归合并函数
export default function mergeObj(target, sources) {
  const obj = target
  if (typeof target !== 'object' || typeof sources !== 'object') {
    return sources // 如果其中一个不是对象 就返回sources
  }
  for (const key in sources) {
    // 如果target也存在 那就再次合并
    if (Object.prototype.hasOwnProperty.call(target, key)) {
      obj[key] = mergeObj(target[key], sources[key])
    } else {
      // 不存在就直接添加
      obj[key] = sources[key]
    }
  }
  return obj
}

step 2: 引用 在相应的组件中引用,然后直接使用函数:

<script>
import mergeObj from '../../utils/mergeObj.js'
export default {
  // ... 

  methods: {
    // ...
  const option = mergeObj(obj1, obj2)
}
</script>
Load Disqus Comments…