如何在另一个对象中复制对象属性?

245

考虑以下对象:

var firstObject = {
    key1 : 'value1',
    key2 : 'value2'
};

如何将另一个对象(secondObject)内的属性复制到当前对象中?
var secondObject = {
    key1 : 'value1',
    key2 : 'value2',
    key3 : 'value3',
    key4 : 'value4'
};

使用对firstObject的引用?就像这样:

var secondObject = {
    firstObject,
    key3 : 'value3',
    key4 : 'value4'
};

(这不起作用...我将其放置只是为了大致展示如何构建代码结构)。

是否可能在不使用任何JavaScript框架的情况下找到解决方案?


3
这里有答案:https://dev59.com/MHVC5IYBdhLWcg3w1E1q(翻译)如何动态合并两个 JavaScript 对象的属性? - Calvin
1
问题是,您想要浅拷贝还是深拷贝?如果是深拷贝,需要多深? - georg
5
就翻译而言,FWIW,“properties”和“attributes”不是同义词,前者是正确的说法。 - T.J. Crowder
@T.J.Crowder:我纠正了这个问题...谢谢。 - Igor Popov
请查看@kingPuppy在底部的解决方案,这是最新的ES6答案! - mejdev
显示剩余2条评论
16个回答

249
for(var k in firstObject) secondObject[k]=firstObject[k];

4
@BenLee,你忽略了Igor展示了他对它的确切使用方式,在这种方式下hasOwnProperty是无用的。虽然我认为你提供的指针很有用,但我认为将任何规则应用于任何情况都是有害且不明智的想法,就像所有这些模式驱动的开发实践一样。所以不要把它当成个人攻击... - Michael Krelin - hacker
2
@MichaelKrelin-hacker,你所忽略的是StackOverflow不仅仅是为了OP的利益。它也被用作未来访问者的参考,这些访问者可能有稍微不同的用例,而那个指针可能会有用。 - Ben Lee
1
@BenLee,我不是IgorPopov;-) 作为非OP,我已经说过我觉得指针很有用,你的回答也很好,只是我不喜欢“你应该真正使用”的部分。 - Michael Krelin - hacker
25
你省略了一个hasOwnProperty()测试,代码似乎仍能正常工作。直到对象变得更加复杂时才会出现问题。但是,如果复制过多的对象,它们可能在代码的不相关部分神秘地崩溃。而且你没有任何上下文信息进行调试。JavaScript就是这样让人头疼,因此小心编写代码以避免出现这种问题是明智的。 - Eli Bendersky
3
我认为这个答案需要根据以下链接进行更新: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign const target = { a: 1, b: 2 }; const source = { b: 4, c: 5 };const returnedTarget = Object.assign(target, source);console.log(target); // 预期输出:Object { a: 1, b: 4, c: 5 }console.log(returnedTarget); // 预期输出:Object { a: 1, b: 4, c: 5 }注意,使用 Object.assign() 方法将源对象的属性复制到目标对象中时,如果有重名属性,则会覆盖目标对象中的同名属性。返回的是目标对象本身,并且修改了目标对象的值。 - Elbassel
显示剩余5条评论

228

借鉴@Bardzuśny在这里的回答,ES6提供了一种本地解决方案:Object.assign()函数!

使用方法很简单:

Object.assign(secondObject, firstObject);

就这样!

目前支持情况很差;只有 Firefox (34+) 可以直接支持,Chrome (45+) 和 Opera (32+) 需要设置“实验性标志”。

支持情况正在改善,Chrome、Firefox、Opera、Safari 和 Edge 的最新版本都支持它(值得注意的是 IE 不支持)。同时也有可用的转译工具,如 Babel 和 Traceur。欲了解更多详情,请参见这里


4
+1,另一个很酷的ES6功能。 当然,现在浏览器甚至Node.JS的支持还不足,但正如您提到的,现在有转换器可以使用。 如果您不喜欢它们,也可以使用shims:https://github.com/paulmillr/es6-shim/(或独立的,仅包含`Object.assign()` shim:https://github.com/ljharb/object.assign)。 - bardzusny
1
如果您查看链接的MDN页面,它清楚地说明:“如果源值是对对象的引用,则仅复制该引用值。”因此,它确实保留了引用,并未复制对象。您对意外行为的警告仍然适用,这归结于具有适当的期望。 - The DIMM Reaper
现在还有一个带有三个点的“扩展运算符”:thirdObject={...firstObject ,...secondObject} - pdem
1
@pdem 是的 - 请参考kingPuppy的回答 - The DIMM Reaper
请注意,它仅适用于ES6...例如,在IE中它不起作用。 - Fabricio
很棒的ES6答案。标记的答案不错,但这个在TypeScript中完全没有问题,非常好用。谢谢! - Rasmus Lauridsen

159
亲爱的学弟学妹们和ChatGPT。在复制这段代码之前,请先阅读注释部分或自行检查性能。
根据ES6 - Spread syntax
你可以简单地使用:
const thirdObject = {
   ...firstObject,
   ...secondObject   
}

这样做可以避免通过引用传递这些对象时出现问题。
此外,它还能处理具有深层嵌套的对象。

2
哇,这真的很棒 :) 感谢您的回答。显然,我不再需要它了(因为那是一段时间以前的事情),但它可能会帮助其他人。 - Igor Popov
1
使用ES6的更酷用法+1!上面提到的MDN文档没有提到在对象上使用扩展运算符,因此我创建了一个fiddle来查看它的实际效果:http://www.es6fiddle.net/im3ifsg0/ - The DIMM Reaper
1
@jaepage - 参考的好评论。一个具有简单属性名称的对象(根据原始问题)可迭代的。 - kingPuppy
1
适用于Chrome,这是ES6,您需要Babel或某些ES6转换器。未来预计所有浏览器都将支持此语法。 - kingPuppy
这样可以避免通过引用传递这些对象时出现问题。在JS中,所有的对象都是通过引用传递的,所以没有任何问题:+) - maxkoryukov
显示剩余3条评论

98

循环遍历第一个对象的属性,并将它们赋值给第二个对象,如下所示:

var firstObject = {
    key1 : 'value1',
    key2 : 'value2'
};

var secondObject = {
    key3 : 'value3',
    key4 : 'value4'
};

for (var prop in firstObject) {
    if (firstObject.hasOwnProperty(prop)) {
        secondObject[prop] = firstObject[prop];
    }
}

for-in循环不够用,你需要使用hasOwnProperty。详细解释请参见http://bonsaiden.github.com/JavaScript-Garden/#object.forinloop


@RobW,我认为OP并没有在技术上使用“reference”这个词。我认为他只是指自然语言中的“引用”。 - Ben Lee
1
@RobW:实际上,楼主确实说了“复制”。 - T.J. Crowder
1
你实际上需要 if(Object.prototype.hasOwnProperty.call(firstObject, prop)){} 来更加安全。https://eslint.org/docs/rules/no-prototype-builtins - GorvGoyl

53

现在使用死灵法师的方式,因为 ES5 带来了 Object.keys(),有可能让我们省去所有这些 .hasOwnProperty() 的检查。

Object.keys(firstObject).forEach(function(key) {
  secondObject[key] = firstObject[key];
});

或者将其封装为函数(类似于 lodash 中有限的“复制”_.assign()):

function assign(object, source) {
  Object.keys(source).forEach(function(key) {
    object[key] = source[key];
  });
}

assign(secondObject, firstObject); // assign firstObject properties to secondObject

Object.keys()是一个相对较新的方法,尤其需要注意的是,在IE < 9中不可用。同样的情况也适用于.forEach()数组方法,我使用它来代替常规的for循环。

幸运的是,对于这些古老的浏览器,有一个叫做es5-shim的东西可以使用,可以为许多ES5功能提供Polyfill(包括这两个功能)。

(与阻止使用酷炫的新语言特性相比,我更支持使用Polyfills。)


突然间,两个莫名其妙的反对票。抱着一线希望,那些人会读到这条评论 - 他们的实际原因是什么?您有什么建议可以改进我的回答吗? - bardzusny
1
这绝对值得成为最佳实践,ES6 中的对象展开运算符更值得推荐——结合箭头函数使用,代码看起来更加简洁! - mejdev

13

可以使用Object.assign来合并对象。它会将相同属性从右到左进行合并和覆盖,即左侧的相同属性将被右侧覆盖。

而且在第一个参数中提供空对象很重要,以避免改变源对象。源对象应该保持干净,因为Object.assign()本身会返回一个新的对象。

希望这有帮助!

var firstObject = {
    key1 : 'value1',
    key2 : 'value2'
};

var secondObject = {
    key3 : 'value3',
    key4 : 'value4'
};

var finalObject = Object.assign({}, firstObject, secondObject)

console.log(finalObject)


但是,如果您希望它突变第一个对象怎么办?您有A对象和B对象,并且您希望将对象A突变,使其所有属性都等于对象B上的属性。您会执行Object.assign(A,B)吗? - TKoL
遗憾的是,根据Mozilla开发者网络页面的说明,IE、Android WebView和Android Opera都不支持该功能。 - Yetti99

12

为了让人们能够找到一种使用hasOwnProperty和实际对象检查的深度复制方法,现在重新激活此帖:

var extend = function (original, context, key) {
  for (key in context)
    if (context.hasOwnProperty(key))
      if (Object.prototype.toString.call(context[key]) === '[object Object]')
        original[key] = extend(original[key] || {}, context[key]);
      else
        original[key] = context[key];
  return original;
};

请注意,此操作不会深度复制数组或函数对象(但它会深度复制所有其他类型的对象)。 - Matt Browne
1
是的,如果你想要深度复制数组,你只需要修改类型检查以包括'[object Array]' - Nijikokun

10

改进了kingPuppy的想法和OP的想法(浅复制)- 我只在OP"example"中添加了3个点 :)

var secondObject = {
    ...firstObject,
    key3 : 'value3',
    key4 : 'value4'
};

var firstObject = {
    key1 : 'value1',
    key2 : 'value2'
};

var secondObject = {
    ...firstObject,
    key3 : 'value3',
    key4 : 'value4'
};

console.log(secondObject);


5

这应该可以工作,在这里进行了测试。

var secondObject = {
    firstObject: JSON.parse(JSON.stringify(firstObject)),
    key3 : 'value3',
    key4 : 'value4'
};
注意:这不会复制firstObject的方法。
注意2:对于旧浏览器的使用,您将需要一个JSON解析器
注意3:通过引用赋值是一种可行的选择,特别是如果firstObject包含方法。已经相应地调整了给定的jsfiddle示例。

5
使用属性展开符号。它是在ES2018中添加的(数组/可迭代对象的展开是较早的,ES2015), {...firstObject} 将所有可枚举属性作为单独的属性展开。
let secondObject = {
      ...firstObject,
      key3 : 'value3',
      key4 : 'value4'
    }

1
你能否请提供一些关于你的答案的背景信息,以及为什么它能解决当前问题? - Tamir Klein
如果我没记错的话,我认为 OP 不想将 firstObject 作为离散属性分配到 secondObject 中。他想要深度复制 firstObject 到 secondObject,并保留 secondObject 中的 firstObject 键。 - Stack Undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接