如何将多个JavaScript对象的属性连接起来

154

我正在寻找最佳方法来“添加”多个JavaScript对象(关联数组)。

例如,给定:

a = { "one" : 1, "two" : 2 };
b = { "three" : 3 };
c = { "four" : 4, "five" : 5 };

什么是计算的最佳方式:

{ "one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

3
语义注释:尽管使用了 [] 语法,但它们实际上并不是数组。顺序并不能完全保证。 - Álvaro González
2
迭代顺序不受ECMA标准保证。但是在大多数浏览器中,它的实现方式是这样的。(来自John Resig)ECMAScript规范明确未定义此行为。在ECMA-262第12.6.4节中:枚举属性的机制...取决于实现。然而,规范与实现相当不同。所有现代ECMAScript实现都按照定义时的顺序迭代对象属性。因此,Chrome团队认为这是一个错误,并将对其进行修复。 - Ruan Mendes
14个回答

223

ECMAscript 6引入了Object.assign()方法,来在Javascript中实现这个功能。

Object.assign()方法用于将一个或多个源对象的所有可枚举自有属性的值复制到目标对象。它将返回目标对象。

MDN文档关于 Object.assign()

var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };

var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }

Object.assign 能在许多现代浏览器中使用,但并非所有浏览器都支持。使用类似 BabelTraceur 这样的转译器来生成向后兼容的 ES5 JavaScript 代码。


6
这是我能说的最好的话。由于现在大多数人使用E6,Object.assign是最好的答案。 - Vimalraj Selvam
9
如果你不知道需要合并多少个对象,因为它们在一个数组中,你可以按如下方式合并它们:Object.assign.apply({}, [{a: 1}, {b: 2}, ....]) - Jeanluca Scaljeri
3
合并对象数组的替代方法是使用扩展运算符:Object.assign( ...objects ),而不是使用 Object.assign.apply - Spen
@Spen已经在这个问题的答案中包含了它。我不认为在这里重复它有任何好处。 - filoxo
4个小时后,这个回答解决了我的Angular 7问题。感谢您的简洁和准确。 - Gel
多好的答案! - suchislife

70

ECMAScript 6拥有扩展语法。现在,您可以这样做:

const obj1 = { 1: 11, 2: 22 };
const obj2 = { 3: 33, 4: 44 };
const obj3 = { ...obj1, ...obj2 };

console.log(obj3); // {1: 11, 2: 22, 3: 33, 4: 44}


3
这应该是被接受的答案,因为它是最现代化的。Object.assign现在已经过时了,不如这个易读。 - rugk
1
没错,但如果你有一个对象数组呢?使用assign,你可以这样做:Object.assign({}, ...objcs) 或者 Object.assign.apply({}, objcs) - Hexception

35
这应该就可以了:

function collect() {
  var ret = {};
  var len = arguments.length;
  for (var i = 0; i < len; i++) {
    for (p in arguments[i]) {
      if (arguments[i].hasOwnProperty(p)) {
        ret[p] = arguments[i][p];
      }
    }
  }
  return ret;
}

let a = { "one" : 1, "two" : 2 };
let b = { "three" : 3 };
let c = { "four" : 4, "five" : 5 };

let d = collect(a, b, c);
console.log(d);

输出:

{
  "one": 1,
  "two": 2,
  "three": 3,
  "four": 4,
  "five": 5
}

1
是的,没错。缓存长度会提高性能。但是,由于参数“array”的大小不太可能非常大,所以在这种情况下并不重要。 - jhurshman
1
不,除了在IE6上稍有不同。访问length属性的成本与访问len变量相同。 - Alsciende
1
@Juan,我认为你是错误的,我进行了一些测试来确定我的想法。缓存长度是一种过时的优化神话,已经过时多年了,而且它使代码(稍微)不太易读。实际上,缓存长度有时会减慢浏览器(Safari)的速度。 - Alsciende
2
写成'for (var p in...'而不是'for (p in...'会更好吧? - Hugo
不应该写 for (var i=0; i<len; i++),因为这会暗示计数器循环变量 i 是一个局部变量,其作用域仅限于循环内部,这会让人感到困惑,因为 JS 没有这样的局部变量概念。相反,i(以及当前未声明的 p)应该在函数开头声明。 - Gerd Wagner
显示剩余3条评论

34

你可以像这样使用jQuery的$.extend

let a = { "one" : 1, "two" : 2 },
    b = { "three" : 3 },
    c = { "four" : 4, "five" : 5 };

let d = $.extend({}, a, b, c)

console.log(d)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


即使您不使用jQuery的方法,也可以使用他们的代码来帮助构建自己的(可能更具体的)实现。 - tvanfosson
26
@Randal:不使用jQuery有很多完全合理的理由。 - Tim Down
3
编辑:d = $.extend({},a,b,c); - Bob Stein

12

Underscore有一些方法可以实现这个功能;

1. _.extend(destination, *sources)

源对象中的所有属性复制到目标对象中,并返回目标对象

_.extend(a, _.extend(b, c));
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }
或者
_.extend(a, b);
=> {"one" : 1, "two" : 2, "three" : 3}
_.extend(a, c);
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

2. _.defaults(object, *defaults)

该函数用来将defaults对象中的属性值填充到object对象中未定义或为undefined的属性中,然后返回object对象。

_.defaults(a, _.defaults(b, c));
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

或者

_.defaults(a, b);
=> {"one" : 1, "two" : 2, "three" : 3}
_.defaults(a, c);
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

8
现在可以使用比Object.assign()更短的语法来浅克隆(不包括原型)或合并对象。 ECMAScript 2018 引入了对象字面量扩展语法
const a = { "one": 1, "two": 2 };
const b = { "three": 3 };
const c = { "four": 4, "five": 5 };

const result = {...a, ...b, ...c};
// Object { "one": 1, "two": 2 , "three": 3, "four": 4, "five": 5 }

Spread (...) operator许多现代浏览器中得到支持,但并非所有浏览器都支持。

因此,建议使用转译器(如Babel)将 ECMAScript 2015+ 代码转换为当前和旧版浏览器或环境中向后兼容的 JavaScript 版本。

这是Babel将为您生成的等效代码:

"use strict";

var _extends = Object.assign || function(target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i];
    for (var key in source) {
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        target[key] = source[key];
      }
    }
  }
  return target;
};

var a = { "one": 1, "two": 2 };
var b = { "three": 3 };
var c = { "four": 4, "five": 5 };

var result = _extends({}, a, b, c);
// Object { "one": 1, "two": 2 , "three": 3, "four": 4, "five": 5 }

这个回答比被接受的回答更健壮。谢谢! - Kaleb Anderson

5
为了合并不定数量的对象,我们可以使用 Object.assign展开语法
const mergeObjs = (...objs) => Object.assign({}, ...objs);

上述函数接受任意数量的对象,将它们所有的属性合并到一个新对象中,并且后面对象的属性会覆盖前面对象的属性。
演示:

const mergeObjs = (...objs) => Object.assign({}, ...objs);
const a = {prop: 1, prop2: '2'},
      b = {prop3: 3, prop4: [1,2,3,4]}
      c = {prop5: 5},
      d = {prop6: true, prop7: -1},
      e = {prop1: 2};
const abcd = mergeObjs(a,b,c,d);
console.log("Merged a,b,c,d:", abcd);
const abd = mergeObjs(a,b,d);
console.log("Merged a,b,d:", abd);
const ae = mergeObjs(a,e);//prop1 from e will overwrite prop1 from a
console.log("Merged a,e:", ae);

要合并对象数组,可以应用类似的方法。

const mergeArrayOfObjs = arr => Object.assign({}, ...arr);

演示:

const mergeArrayOfObjs = arr => Object.assign({}, ...arr);
const arr = [
  {a: 1, b: 2},
  {c:1, d:3},
  {abcd: [1,2,3,4], d: 4}
];
const merged = mergeArrayOfObjs(arr);
console.log(merged);


4
为什么函数应该限制为3个参数?同时,检查是否有"hasOwnProperty"。
function Collect() {
    var o={};
    for(var i=0;i<arguments.length;i++) {
      var arg=arguments[i];
      if(typeof arg != "object") continue;
      for(var p in arg) {
        if(arg.hasOwnProperty(p)) o[p] = arg[p];
      }
    }
    return o;
}

4
使用ES7扩展运算符很容易将对象展开。在浏览器控制台中输入:
({ name: "Alex", ...(true  ? { age: 19 } : { })}) // {name: "Alex", age: 19}
({ name: "Alex", ...(false ? { age: 19 } : { })}) // {name: "Alex",        }

1
不错。这个问题已经超过10年了,很高兴现在这样简单的操作变得容易了。如果你看其他人的答案,以前并不那么容易。 - Vlad
这就是为什么我留下了自己的答案 :) - Purkhalo Alex

3

ES6 ++

这个问题是将各种不同的对象合并为一个。

let obj = {};
const obj1 = { foo: 'bar' };
const obj2 = { bar: 'foo' };
Object.assign(obj, obj1, obj2);
//output => {foo: 'bar', bar: 'foo'};

假设您有一个带有多个键的对象,这些键都是对象:

let obj = {
  foo: { bar: 'foo' },
  bar: { foo: 'bar' }
}

这是我找到的解决方案(仍需使用foreach :/)。
let objAll = {};

Object.values(obj).forEach(o => {
  objAll = {...objAll, ...o};
});

通过这样做,我们可以动态地将所有对象键添加到一个中。

// Output => { bar: 'foo', foo: 'bar' }

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