Как динамически объединить свойства двух объектов JavaScript?

Мне нужно иметь возможность объединить два (очень простых) объекта JavaScript во время выполнения. Например, я хотел бы:

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }

obj1.merge(obj2);

//obj1 now has three properties: food, car, and animal

У кого-нибудь есть сценарий для этого или знаете встроенный способ сделать это? Мне не нужна рекурсия, и мне не нужно объединять функции, только методы на плоских объектах.

5.10.2008 00:30:54
Стоит отметить этот ответ на аналогичный вопрос , который показывает, как объединить «один уровень вниз». Таким образом, он объединяет значения дубликатов ключей (вместо того, чтобы перезаписывать первое значение вторым значением), но не обрабатывает дальше, чем это. ИМХО, это хороший чистый код для этой задачи.
ToolmakerSteve 3.10.2019 21:42:41
Кстати, первые несколько ответов выполняют «поверхностное» слияние: если один и тот же ключ существует в obj1 и obj2, значение в obj2 сохраняется, значение в obj1 удаляется. Например, если бы пример вопроса был var obj2 = { animal: 'dog', food: 'bone' };, слияние было бы { food: 'bone', car: 'ford', animal: 'dog' }. Если вы работаете с «вложенными данными» и хотите «глубокое слияние», найдите ответы, в которых упоминается «глубокое слияние» или «рекурсия». Если у вас есть значения, которые есть arrays, то используйте опцию «arrayMerge» в github «TehShrike / deepmerge», как упомянуто здесь .
ToolmakerSteve 4.10.2019 12:30:07
Пожалуйста, перейдите по ссылке ниже stackoverflow.com/questions/171251/… Оператор распространения, который является новой функцией в версии
Soura Sankar Ghosh 11.01.2020 13:00:12
30 ОТВЕТОВ
РЕШЕНИЕ

ECMAScript 2018 Стандартный метод

Вы бы использовали объект распространения :

let merged = {...obj1, ...obj2};

mergedтеперь союз obj1и obj2. Свойства в obj2перезапишут те в obj1.

/** There's no limit to the number of objects you can merge.
 *  Later properties overwrite earlier properties with the same name. */
const allRules = {...obj1, ...obj2, ...obj3};

Вот также документация MDN для этого синтаксиса. Если вы используете babel, вам понадобится плагин babel-plugin-transform-object-rest-spread для его работы.

ECMAScript 2015 (ES6) Стандартный метод

/* For the case in question, you would do: */
Object.assign(obj1, obj2);

/** There's no limit to the number of objects you can merge.
 *  All objects get merged into the first object. 
 *  Only the object in the first argument is mutated and returned.
 *  Later properties overwrite earlier properties with the same name. */
const allRules = Object.assign({}, obj1, obj2, obj3, etc);

(см. MDN JavaScript Reference )


Метод для ES5 и ранее

for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }

Обратите внимание , что это будет просто добавить все атрибуты obj2для obj1которых не может быть то , что вы хотите , если вы все еще хотите использовать неизмененный obj1.

Если вы используете фреймворк, охватывающий все ваши прототипы, вам придется потрудиться с подобными проверками hasOwnProperty, но этот код будет работать в 99% случаев.

Пример функции:

/**
 * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
 * @param obj1
 * @param obj2
 * @returns obj3 a new object based on obj1 and obj2
 */
function merge_options(obj1,obj2){
    var obj3 = {};
    for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
    for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
    return obj3;
}
2845
20.04.2020 09:19:22
Это не работает, если объекты имеют одинаковые атрибуты имени, и вы также хотели бы объединить атрибуты.
Xiè Jìléi 24.10.2010 10:56:19
Это делает только поверхностное копирование / слияние. Имеет потенциал, чтобы заглушить много элементов.
Jay Taylor 2.06.2011 15:39:52
+1 за признание того, что некоторые бедные души вынуждены использовать фреймворки, которые портят все их прототипы ...
thejonwithnoh 7.05.2016 04:18:07
Это не сработало для меня из-за того, что «поэтому он присваивает свойства вместо простого копирования или определения новых свойств. Это может сделать его непригодным для объединения новых свойств в прототип, если источники слияния содержат геттеры». ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ). Я должен был использовать var merged = {...obj1, ...obj2}.
morgler 10.01.2018 20:20:24
@ Ze'ev{...obj1, ...{animal: 'dog'}}
backslash112 11.04.2019 21:13:19

У jQuery также есть утилита для этого: http://api.jquery.com/jQuery.extend/ .

Взято из документации по jQuery:

// Merge options object into settings object
var settings = { validate: false, limit: 5, name: "foo" };
var options  = { validate: true, name: "bar" };
jQuery.extend(settings, options);

// Now the content of settings object is the following:
// { validate: true, limit: 5, name: "bar" }

Приведенный выше код будет мутировать существующий объект с именем settings.


Если вы хотите создать новый объект без изменения любого аргумента, используйте это:

var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };

/* Merge defaults and options, without modifying defaults */
var settings = $.extend({}, defaults, options);

// The content of settings variable is now the following:
// {validate: true, limit: 5, name: "bar"}
// The 'defaults' and 'options' variables remained the same.
1187
19.07.2018 18:28:10
Осторожно: переменная «настройки» будет изменена. JQuery не возвращает новый экземпляр. Причина этого (и именования) заключается в том, что .extend () был разработан для расширения объектов, а не для того, чтобы разбирать вещи вместе. Если вы хотите новый объект (например, настройки - это настройки по умолчанию, к которым вы не хотите прикасаться), вы всегда можете jQuery.extend ({}, настройки, опции);
webmat 4.05.2011 16:01:48
Право на! Огромное спасибо. Как говорилось в предыдущем комментарии, он плохо назван. Я искал документы JQuery назад и четвертый и не столкнулся с ним.
Mike Starov 2.06.2011 20:30:17
Имейте в виду, jQuery.extend также имеет глубокий (логический) параметр. jQuery.extend(true,settings,override), что важно, если свойство в настройках содержит объект, а переопределение имеет только часть этого объекта. Вместо удаления несогласованных свойств глубокий параметр будет обновляться только там, где он существует. По умолчанию установлено значение false.
vol7ron 9.06.2011 21:09:38
Ну и дела, они действительно сделали хорошую работу, назвав это. Если вы ищете, как расширить объект Javascript, он подходит! Вы можете не столкнуться с этим, хотя, если вы пытаетесь соединить или соединить объекты Javascript.
Derek Greer 20.05.2014 18:43:01
Не говорите людям об использовании библиотечного метода, когда пара строк vanilla JS будет делать то, что они хотят. Было время, прежде чем JQuery!
CrazyMerlin 11.08.2016 18:01:18

Harmony ECMAScript 2015 (ES6) указывает , Object.assignкоторый будет делать это.

Object.assign(obj1, obj2);

Текущая поддержка браузеров становится лучше , но если вы разрабатываете для браузеров, которые не поддерживают, вы можете использовать полифилл .

344
8.08.2016 19:12:47
Обратите внимание, что это только поверхностное слияние
Joachim Lous 16.03.2016 10:04:34
Я думаю, что тем временем Object.assignимеет довольно приличное освещение: kangax.github.io/compat-table/es6/…
LazerBass 26.05.2016 15:38:17
Теперь это должен быть правильный ответ. В настоящее время люди компилируют свой код для совместимости с редким браузером (IE11), который не поддерживает подобные вещи. Примечание: если вы не хотите добавлять obj2 в obj1, вы можете вернуть новый объект, используяObject.assign({}, obj1, obj2)
Duvrai 21.07.2016 20:02:08
@Duvrai Согласно отчетам, которые я видел, IE11 определенно не редкость и составляет около 18% доли рынка по состоянию на июль 2016 года.
NanoWizard 8.08.2016 19:15:43
@Kermani ОП особо отмечает, что рекурсия не нужна. Поэтому, хотя полезно знать, что это решение выполняет поверхностное объединение, этого ответа достаточно, как есть. Комментарий JoachimLou получил заслуженные отклики и предоставляет дополнительную информацию при необходимости. Я думаю, что разница между глубоким и мелким слиянием и схожими темами выходит за рамки этого обсуждения и лучше подходит для других вопросов SO.
NanoWizard 20.08.2016 16:42:47

Я гуглил код для слияния свойств объекта и оказался здесь. Однако, поскольку не было никакого кода для рекурсивного слияния, я написал его сам. (Может быть, расширение jQuery является рекурсивным BTW?) В любом случае, надеюсь, кто-то еще найдет это полезным.

(Сейчас код не использует Object.prototype:)

Код

/*
* Recursively merge properties of two objects 
*/
function MergeRecursive(obj1, obj2) {

  for (var p in obj2) {
    try {
      // Property in destination object set; update its value.
      if ( obj2[p].constructor==Object ) {
        obj1[p] = MergeRecursive(obj1[p], obj2[p]);

      } else {
        obj1[p] = obj2[p];

      }

    } catch(e) {
      // Property in destination object not set; create it and set its value.
      obj1[p] = obj2[p];

    }
  }

  return obj1;
}

Пример

o1 = {  a : 1,
        b : 2,
        c : {
          ca : 1,
          cb : 2,
          cc : {
            cca : 100,
            ccb : 200 } } };

o2 = {  a : 10,
        c : {
          ca : 10,
          cb : 20, 
          cc : {
            cca : 101,
            ccb : 202 } } };

o3 = MergeRecursive(o1, o2);

Создает объект o3 like

o3 = {  a : 10,
        b : 2,
        c : {
          ca : 10,
          cb : 20,
          cc : { 
            cca : 101,
            ccb : 202 } } };
262
9.05.2013 15:16:33
Хорошо, но я бы сначала сделал глубокую копию объектов. Таким образом, o1 также будет изменен, так как объекты передаются по ссылке.
skerit 16.01.2011 16:00:46
Это было то, что я искал. Не забудьте проверить, что obj2.hasOwnProperty (p) перед тем, как перейти к оператору try catch. В противном случае вы можете получить какую-то другую чушь, сливающуюся сверху в цепочку прототипов.
Adam 9.07.2011 02:25:24
Спасибо Маркус. Обратите внимание, что o1 также модифицируется MergeRecursive, поэтому в конце o1 и o3 идентичны. Это может быть более интуитивно понятно, если вы ничего не возвращаете, поэтому пользователь знает, что то, что он предоставляет (o1), изменяется и становится результатом. Кроме того, в вашем примере, возможно, стоит добавить что-то в o2, которое изначально не было в o1, чтобы показать, что свойства o2 вставляются в o1 (кто-то еще ниже представил альтернативный код, копирующий ваш пример, но их разрывает, когда o2 содержит свойства, не в о1 - но ваш работает в этом отношении).
pancake 24.04.2012 05:40:23
Это хороший ответ, но пара утешает: 1) никакая логика не должна основываться на исключениях; в этом случае любое выброшенное исключение будет обнаружено с непредсказуемым результатом. 2) Я согласен с комментарием @ pancake о том, что ничего не возвращается, поскольку исходный объект изменен. Вот пример jsFiddle без логики исключений: jsfiddle.net/jlowery2663/z8at6knn/4 - Джефф Лоури
Jeff Lowery 25.11.2014 21:14:18
Кроме того, obj2 [p] .constructor == Массив необходим в любом случае, когда существует массив объектов.
The Composer 15.12.2015 19:27:40

Обратите внимание , что underscore.js«S extend-метод делает это в однострочнике:

_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}
175
25.10.2016 21:39:21
Этот пример хорош, поскольку вы имеете дело с анонимными объектами, но если это не так, то здесь применяется комментарий webmat в ответе jQuery с предупреждением о мутациях, так как подчеркивание также изменяет целевой объект . Как и в ответе jQuery, чтобы сделать это без мутаций, просто слиться с пустым объектом:_({}).extend(obj1, obj2);
Abe Voelker 5.06.2012 18:12:06
Есть еще один, который может быть релевантным в зависимости от того, чего вы пытаетесь достичь: _.defaults(object, *defaults)«Заполните нулевые и неопределенные свойства в объекте значениями из объектов по умолчанию и верните объект».
conny 10.12.2012 16:08:55
Многие люди прокомментировали изменение объекта назначения, но вместо того, чтобы метод создавал новый, общий шаблон (например, в dojo) состоит в том, чтобы сказать dojo.mixin (dojo.clone (myobj), {newpropertys: "добавить to myobj "})
Colin D 26.03.2016 02:57:06
Подчеркивание может занимать произвольное количество объектов, поэтому вы можете избежать мутации исходного объекта var mergedObj = _.extend({}, obj1, obj2).
lobati 26.04.2016 02:49:02

Подобно jQuery extend (), у вас есть такая же функция в AngularJS :

// Merge the 'options' object into the 'settings' object
var settings = {validate: false, limit: 5, name: "foo"};
var options  = {validate: true, name: "bar"};
angular.extend(settings, options);
84
25.10.2016 21:52:40
@naXa Я думаю, что будет вариант, если вы не хотите добавлять библиотеку только для этого fn.
juanmf 22.03.2016 13:10:55

Мне нужно объединить объекты сегодня, и этот вопрос (и ответы) мне очень помог. Я попробовал некоторые ответы, но ни один из них не соответствовал моим потребностям, поэтому я соединил некоторые ответы, сам что-то добавил и предложил новую функцию слияния. Вот:

var merge = function() {
    var obj = {},
        i = 0,
        il = arguments.length,
        key;
    for (; i < il; i++) {
        for (key in arguments[i]) {
            if (arguments[i].hasOwnProperty(key)) {
                obj[key] = arguments[i][key];
            }
        }
    }
    return obj;
};

Некоторые примеры использования:

var t1 = {
    key1: 1,
    key2: "test",
    key3: [5, 2, 76, 21]
};
var t2 = {
    key1: {
        ik1: "hello",
        ik2: "world",
        ik3: 3
    }
};
var t3 = {
    key2: 3,
    key3: {
        t1: 1,
        t2: 2,
        t3: {
            a1: 1,
            a2: 3,
            a4: [21, 3, 42, "asd"]
        }
    }
};

console.log(merge(t1, t2));
console.log(merge(t1, t3));
console.log(merge(t2, t3));
console.log(merge(t1, t2, t3));
console.log(merge({}, t1, { key1: 1 }));
67
25.10.2016 21:24:33
хороший ответ, но я думаю, что если свойство присутствует в первом и втором, и вы пытаетесь создать новый объект путем слияния первого и второго, то уникальные, присутствующие в первом объекте, будут потеряны
Strikers 20.02.2015 11:41:25
Но разве это не ожидаемое поведение? Цель этой функции - переопределить значения в порядке аргументов. Следующий переопределит текущий. Как вы можете сохранить уникальные ценности? Что вы ожидаете от моего примера merge({}, t1, { key1: 1 })?
Emre Erkan 20.02.2015 11:50:33
Я ожидаю объединения только не типизированных значений.
AGamePlayer 28.02.2015 14:48:51
не удается, если ожидаемый результат выглядит следующим образом: gist.github.com/ceremcem/a54f90923c6e6ec42c7daf24cf2768bd
ceremcem 1.08.2016 01:21:39
Это работало для меня в строгом режиме для моего проекта node.js. Пока что это лучший ответ на этот пост.
klewis 16.03.2018 20:05:36

Вы можете использовать свойства распространения объекта - в настоящее время предложение ECMAScript этапа 3.

const obj1 = { food: 'pizza', car: 'ford' };
const obj2 = { animal: 'dog' };

const obj3 = { ...obj1, ...obj2 };
console.log(obj3);

48
2.02.2017 13:07:59
Поддержка браузера?
Alph.Dev 11.10.2017 10:01:26
@ Alph.Dev Вы знаете, caniuse.com?
connexo 2.01.2018 10:32:28
Я не могу заставить синтаксис с 3 точками работать в node.js. узел жалуется на это.
klewis 16.03.2018 19:52:49

Данные решения должны быть изменены , чтобы проверить source.hasOwnProperty(property)в for..inпетлях до назначения - в противном случае, вы в конечном итоге копирование свойств всей цепи прототипов, которая редко желаемому ...

41
21.12.2008 13:29:04

Объединить свойства N объектов в одну строку кода

Object.assignМетод является частью 2015 (ES6) стандарта ECMAScript и делает именно то , что вам нужно. ( IEне поддерживается)

var clone = Object.assign({}, obj);

Метод Object.assign () используется для копирования значений всех перечисляемых собственных свойств из одного или нескольких исходных объектов в целевой объект.

Читать далее...

Polyfill для поддержки старых браузеров:

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}
40
2.05.2017 10:45:07
Что делает «использование строгого» в старых браузерах или почему оно вообще есть. браузеры, которые поддерживают объект [defineProperty; ключи; getOwnPropertyDescriptor; и т. д.], не совсем старые. Это просто более ранние версии UA 5 поколения.
Bekim Bacaj 20.04.2017 19:39:10
Спасибо за разъяснение, что Objects.assignвозвращает объединенный объект, которого нет в других ответах. Это более функциональный стиль программирования.
Sridhar Sarnobat 9.05.2017 05:59:46

Следующие два, вероятно, являются хорошей отправной точкой. У lodash также есть функция настройки для этих особых нужд!

_.extend( http://underscorejs.org/#extend )
_.merge( https://lodash.com/docs#merge )

36
25.10.2016 21:58:04
Обратите внимание, что вышеуказанные методы видоизменяют исходный объект.
Wtower 1.09.2016 13:39:35
Метод lodash работал для меня, так как я хотел объединить два объекта и сохранить свойства, вложенные глубиной более одного или двух уровней (а не просто заменить / стереть их).
SamBrick 1.09.2017 09:44:45
правда @Wtower. Просто была ошибка из-за этого. Лучше всего убедиться, что он всегда начинается как _.merge ({}, ...
Martin Cremer 10.10.2019 10:09:24

Кстати, все, что вы делаете, это перезаписывает свойства, а не объединяет ...

Вот как действительно объединилась область объектов JavaScript: только ключи в toобъекте, которые сами не являются объектами, будут перезаписаны from. Все остальное будет действительно объединено . Конечно, вы можете изменить это поведение, чтобы не перезаписывать все, что существует, например, если to[n] is undefined, и т. Д ...:

var realMerge = function (to, from) {

    for (n in from) {

        if (typeof to[n] != 'object') {
            to[n] = from[n];
        } else if (typeof from[n] == 'object') {
            to[n] = realMerge(to[n], from[n]);
        }
    }
    return to;
};

Использование:

var merged = realMerge(obj1, obj2);
29
25.10.2016 21:43:42
Мне очень
David Cumps 26.02.2014 08:01:06
Стоит отметить, что typeof array и typeof null вернут 'object' и сломают это.
Salakar 19.07.2016 12:18:00
@ u01jmg3 смотрите мой ответ здесь: stackoverflow.com/questions/27936772/… - функция
Salakar 19.04.2017 12:31:26
Это ломается, если вы используете в строгом режиме для проектов node.js
klewis 16.03.2018 20:01:01

Вот мой удар, который

  1. Поддерживает глубокое слияние
  2. Не мутирует аргументы
  3. Принимает любое количество аргументов
  4. Не расширяет объект-прототип
  5. Не зависит от другой библиотеки ( jQuery , MooTools , Underscore.js и т. Д.)
  6. Включает проверку на hasOwnProperty
  7. Короткий :)

    /*
        Recursively merge properties and return new object
        obj1 &lt;- obj2 [ &lt;- ... ]
    */
    function merge () {
        var dst = {}
            ,src
            ,p
            ,args = [].splice.call(arguments, 0)
        ;
    
        while (args.length > 0) {
            src = args.splice(0, 1)[0];
            if (toString.call(src) == '[object Object]') {
                for (p in src) {
                    if (src.hasOwnProperty(p)) {
                        if (toString.call(src[p]) == '[object Object]') {
                            dst[p] = merge(dst[p] || {}, src[p]);
                        } else {
                            dst[p] = src[p];
                        }
                    }
                }
            }
        }
    
       return dst;
    }

Пример:

a = {
    "p1": "p1a",
    "p2": [
        "a",
        "b",
        "c"
    ],
    "p3": true,
    "p5": null,
    "p6": {
        "p61": "p61a",
        "p62": "p62a",
        "p63": [
            "aa",
            "bb",
            "cc"
        ],
        "p64": {
            "p641": "p641a"
        }
    }
};

b = {
    "p1": "p1b",
    "p2": [
        "d",
        "e",
        "f"
    ],
    "p3": false,
    "p4": true,
    "p6": {
        "p61": "p61b",
        "p64": {
            "p642": "p642b"
        }
    }
};

c = {
    "p1": "p1c",
    "p3": null,
    "p6": {
        "p62": "p62c",
        "p64": {
            "p643": "p641c"
        }
    }
};

d = merge(a, b, c);


/*
    d = {
        "p1": "p1c",
        "p2": [
            "d",
            "e",
            "f"
        ],
        "p3": null,
        "p5": null,
        "p6": {
            "p61": "p61b",
            "p62": "p62c",
            "p63": [
                "aa",
                "bb",
                "cc"
            ],
            "p64": {
                "p641": "p641a",
                "p642": "p642b",
                "p643": "p641c"
            }
        },
        "p4": true
    };
*/
27
18.04.2017 10:21:54
По сравнению с другими, которые я тестировал на этой странице, эта функция действительно рекурсивна (выполняет глубокое слияние) и имитирует то, что jQuery.extend () делает действительно хорошо. Однако было бы лучше, если бы он изменил первый объект / аргумент, чтобы пользователь мог решить, хотят ли они передать пустой объект {}в качестве первого параметра или сделать так, чтобы функция изменила исходный объект. Поэтому , если вы измените dst = {}к dst = arguments[0]ней изменится первый объект , который пройдет в объединенном к объекту
Jasdeep Khalsa 27.03.2015 14:48:34
Теперь он перестал работать в Chrome. Я думаю, что это плохая идея использовать такую ​​конструкцию toString.call(src) == '[object Object]'для проверки, есть ли объект или нет. typeof srcнамного лучше. Более того, он превращает массивы в объект (по крайней мере, у меня такое поведение на последнем Chrome).
m03geek 21.05.2015 14:51:18
Мне нужно было сделать это внутри жадного цикла, я сравнил эту функцию с той, которую я использовал до сих пор _.merge (object, [sources]) из lodash (своего рода подчеркивание lib): ваша функция почти в два раза быстрее Спасибо друг.
Arnaud Bouchot 11.10.2016 14:13:51

Object.assign ()

ECMAScript 2015 (ES6)

Это новая технология, часть стандарта ECMAScript 2015 (ES6). Спецификация этой технологии была завершена, но проверьте таблицу совместимости для использования и состояния реализации в различных браузерах.

Метод 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 }
console.log(o1);  // { a: 1, b: 2, c: 3 }, target object itself is changed.
20
16.11.2015 16:35:04

Для не слишком сложных объектов вы можете использовать JSON :

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog', car: 'chevy'}
var objMerge;

objMerge = JSON.stringify(obj1) + JSON.stringify(obj2);

// {"food": "pizza","car":"ford"}{"animal":"dog","car":"chevy"}

objMerge = objMerge.replace(/\}\{/, ","); //  \_ replace with comma for valid JSON

objMerge = JSON.parse(objMerge); // { food: 'pizza', animal: 'dog', car: 'chevy'}
// Of same keys in both objects, the last object's value is retained_/

Помните, что в этом примере "} {" не должно встречаться в строке!

19
28.10.2017 12:31:07
var so1 = JSON.stringify(obj1); var so2 = JSON.stringify(obj1); objMerge = so1.substr(0, so1.length-1)+","+so2.substr(1); console.info(objMerge);
Quamis 3.09.2013 10:55:01
function merge(obj1, obj2) { return JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)) .replace(/\}\{/g, ',').replace(/,\}/g, '}').replace(/\{,/g, '{')); }
Alex Ivasyuv 4.03.2015 14:54:57
или как однострочник:objMerge = JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)).replace(/\}\{/, ", "));
Rabbi Shuki Gur 20.01.2017 07:06:47
@Charles, извините, но как «этот метод очень опасен», но также и забавен - это максимально безопасно: во-первых, он использует JSON для тяжелой работы по чтению содержимого объекта, во-вторых, он использует объект JSON, который, безусловно, самый безопасный машина оптимизирована для повторного анализа JSO из строкового литерала объекта, совместимого с JSON. И также обратно совместим с IE8, который является первым браузером, который реализует свой стандарт. Это просто заставляет меня задуматься: «Почему ты должен был сказать такую ​​глупость и сойти с рук»?
Bekim Bacaj 15.04.2017 15:49:15
Я полагаю, что функции в объекте будут разрушены, так как stringify не может быть использован, но можем ли мы прояснить другие недостатки?
yeahdixon 25.03.2018 04:23:41

Там есть библиотека называется deepmergeна GitHub : Это , кажется, получает некоторую тягу. Это автономная версия, доступная через менеджеры пакетов npm и bower.

Я был бы склонен использовать или улучшить это вместо того, чтобы копировать код из ответов.

18
25.10.2016 21:56:15

Лучший способ сделать это - добавить правильное свойство, которое нельзя перечислить, используя Object.defineProperty.

Таким образом, вы по-прежнему сможете перебирать свойства ваших объектов, не имея вновь созданного «расширения», которое вы получите, если бы вы создали свойство с Object.prototype.extend.

Надеюсь, это поможет:

Object.defineProperty (Object.prototype, "extend", {
    перечислимый: ложь,
    значение: функция (с) {
        var props = Object.getOwnPropertyNames (from);
        var dest = this;
        props.forEach (function (name) {
            if (имя в dest) {
                var destination = Object.getOwnPropertyDescriptor (from, name);
                Object.defineProperty (dest, name, destination);
            }
        });
        верни это;
    }
});

Как только у вас это заработает, вы можете сделать:

var obj = {
    имя: «стек»,
    отделка: «переполнение»
}
замена var = {
    название: «сток»
};

obj.extend (замена);

Я только что написал пост в блоге об этом здесь: http://onemoredigit.com/post/1527191998/extending-objects-in-node-js

17
9.11.2010 22:09:35
Подождите секунду --- код не работает! :( вставил его в jsperf для сравнения с другими алгоритмами слияния, и код завершился ошибкой - расширение функции не существует
Jens Roland 13.12.2010 14:57:23

Вы можете просто использовать JQuery extend

var obj1 = { val1: false, limit: 5, name: "foo" };
var obj2 = { val2: true, name: "bar" };

jQuery.extend(obj1, obj2);

Теперь obj1содержит все значения obj1иobj2

16
1.03.2016 10:49:04
obj1 будет содержать все значения, а не obj2. Вы расширяете первый объект. jQuery.extend( target [, object1 ] [, objectN ] )
ruuter 21.10.2015 19:33:25
Этот вопрос не о jQuery. JavaScript! = JQuery
Jorge Riv 14.04.2016 21:38:43
Я погуглил для JavaScript, но это более простой подход
Ehsan 28.06.2016 07:00:19

Прототип имеет это:

Object.extend = function(destination,source) {
    for (var property in source)
        destination[property] = source[property];
    return destination;
}

obj1.extend(obj2) будет делать то, что вы хотите.

13
25.10.2016 20:26:12
Вы имели в виду Object.prototype.extend? В любом случае, не рекомендуется расширять Object.prototype. -1.
SolutionYogi 31.07.2009 20:08:50
Если подумать, то, вероятно, должно быть, Object.prototypeа не Object... Хорошая идея или нет, это то, что prototype.jsделает фреймворк, и поэтому, если вы используете его или другой фреймворк, который зависит от него, Object.prototypeон уже будет расширен extend.
ephemient 31.07.2009 21:14:35
Я не хочу говорить вам, но говорю, что это нормально, потому что prototype.js - плохой аргумент. Prototype.js популярен, но это не значит, что они являются примером для подражания в том, как делать JavaScript. Проверьте этот подробный обзор библиотеки Prototype от JS pro. dhtmlkitchen.com/?category=/JavaScript/&date=2008/06/17/…
SolutionYogi 31.07.2009 21:19:33
Кого волнует, правильно это или неправильно? Если это работает, то это работает. Ожидание идеального решения - это здорово, за исключением попыток сохранить контракт, привлечь новых клиентов или уложиться в сроки. Дайте здесь любого, кто увлечен программированием свободных денег, и они в конце концов сделают все «правильно».
David 19.08.2009 04:24:51
Где вы все нашли Object.prototype? Object.extendне будет мешать вашим объектам, он просто присоединяет функцию к Objectобъекту. И obj1.extend(obj2)это не правильный способ срабатывания функции, используй Object.extend(obj1, obj2)insted
artnikpro 3.12.2013 13:23:33

** Объединение объектов простое использование Object.assignили ...оператор распространения **

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog', car: 'BMW' }
var obj3 = {a: "A"}


var mergedObj = Object.assign(obj1,obj2,obj3)
 // or using the Spread operator (...)
var mergedObj = {...obj1,...obj2,...obj3}

console.log(mergedObj);

Объекты объединяются справа налево, это означает, что объекты, которые имеют идентичные свойства с объектами справа, будут переопределены.

В этом примере obj2.carпереопределяетobj1.car

13
30.08.2019 11:49:34

Просто если кто-то использует библиотеку Google Closure :

goog.require('goog.object');
var a = {'a': 1, 'b': 2};
var b = {'b': 3, 'c': 4};
goog.object.extend(a, b);
// Now object a == {'a': 1, 'b': 3, 'c': 4};

Аналогичная вспомогательная функция существует для массива :

var a = [1, 2];
var b = [3, 4];
goog.array.extend(a, b); // Extends array 'a'
goog.array.concat(a, b); // Returns concatenation of array 'a' and 'b'
12
25.10.2016 21:40:54

Я расширил метод Дэвида Коллие:

  • Добавлена ​​возможность объединения нескольких объектов
  • Поддерживает глубокие объекты
  • параметр переопределения (это определяется, если последний параметр является логическим)

Если override имеет значение false, никакое свойство не будет переопределено, но будут добавлены новые свойства.

Использование: obj.merge (сливается ... [, переопределить]);

Вот мой код:

Object.defineProperty(Object.prototype, "merge", {
    enumerable: false,
    value: function () {
        var override = true,
            dest = this,
            len = arguments.length,
            props, merge, i, from;

        if (typeof(arguments[arguments.length - 1]) === "boolean") {
            override = arguments[arguments.length - 1];
            len = arguments.length - 1;
        }

        for (i = 0; i < len; i++) {
            from = arguments[i];
            if (from != null) {
                Object.getOwnPropertyNames(from).forEach(function (name) {
                    var descriptor;

                    // nesting
                    if ((typeof(dest[name]) === "object" || typeof(dest[name]) === "undefined")
                            && typeof(from[name]) === "object") {

                        // ensure proper types (Array rsp Object)
                        if (typeof(dest[name]) === "undefined") {
                            dest[name] = Array.isArray(from[name]) ? [] : {};
                        }
                        if (override) {
                            if (!Array.isArray(dest[name]) && Array.isArray(from[name])) {
                                dest[name] = [];
                            }
                            else if (Array.isArray(dest[name]) && !Array.isArray(from[name])) {
                                dest[name] = {};
                            }
                        }
                        dest[name].merge(from[name], override);
                    } 

                    // flat properties
                    else if ((name in dest && override) || !(name in dest)) {
                        descriptor = Object.getOwnPropertyDescriptor(from, name);
                        if (descriptor.configurable) {
                            Object.defineProperty(dest, name, descriptor);
                        }
                    }
                });
            }
        }
        return this;
    }
});

Примеры и тестовые случаи:

function clone (obj) {
    return JSON.parse(JSON.stringify(obj));
}
var obj = {
    name : "trick",
    value : "value"
};

var mergeObj = {
    name : "truck",
    value2 : "value2"
};

var mergeObj2 = {
    name : "track",
    value : "mergeObj2",
    value2 : "value2-mergeObj2",
    value3 : "value3"
};

assertTrue("Standard", clone(obj).merge(mergeObj).equals({
    name : "truck",
    value : "value",
    value2 : "value2"
}));

assertTrue("Standard no Override", clone(obj).merge(mergeObj, false).equals({
    name : "trick",
    value : "value",
    value2 : "value2"
}));

assertTrue("Multiple", clone(obj).merge(mergeObj, mergeObj2).equals({
    name : "track",
    value : "mergeObj2",
    value2 : "value2-mergeObj2",
    value3 : "value3"
}));

assertTrue("Multiple no Override", clone(obj).merge(mergeObj, mergeObj2, false).equals({
    name : "trick",
    value : "value",
    value2 : "value2",
    value3 : "value3"
}));

var deep = {
    first : {
        name : "trick",
        val : "value"
    },
    second : {
        foo : "bar"
    }
};

var deepMerge = {
    first : {
        name : "track",
        anotherVal : "wohoo"
    },
    second : {
        foo : "baz",
        bar : "bam"
    },
    v : "on first layer"
};

assertTrue("Deep merges", clone(deep).merge(deepMerge).equals({
    first : {
        name : "track",
        val : "value",
        anotherVal : "wohoo"
    },
    second : {
        foo : "baz",
        bar : "bam"
    },
    v : "on first layer"
}));

assertTrue("Deep merges no override", clone(deep).merge(deepMerge, false).equals({
    first : {
        name : "trick",
        val : "value",
        anotherVal : "wohoo"
    },
    second : {
        foo : "bar",
        bar : "bam"
    },
    v : "on first layer"
}));

var obj1 = {a: 1, b: "hello"};
obj1.merge({c: 3});
assertTrue(obj1.equals({a: 1, b: "hello", c: 3}));

obj1.merge({a: 2, b: "mom", d: "new property"}, false);
assertTrue(obj1.equals({a: 1, b: "hello", c: 3, d: "new property"}));

var obj2 = {};
obj2.merge({a: 1}, {b: 2}, {a: 3});
assertTrue(obj2.equals({a: 3, b: 2}));

var a = [];
var b = [1, [2, 3], 4];
a.merge(b);
assertEquals(1, a[0]);
assertEquals([2, 3], a[1]);
assertEquals(4, a[2]);


var o1 = {};
var o2 = {a: 1, b: {c: 2}};
var o3 = {d: 3};
o1.merge(o2, o3);
assertTrue(o1.equals({a: 1, b: {c: 2}, d: 3}));
o1.b.c = 99;
assertTrue(o2.equals({a: 1, b: {c: 2}}));

// checking types with arrays and objects
var bo;
a = [];
bo = [1, {0:2, 1:3}, 4];
b = [1, [2, 3], 4];

a.merge(b);
assertTrue("Array stays Array?", Array.isArray(a[1]));

a = [];
a.merge(bo);
assertTrue("Object stays Object?", !Array.isArray(a[1]));

a = [];
a.merge(b);
a.merge(bo);
assertTrue("Object overrides Array", !Array.isArray(a[1]));

a = [];
a.merge(b);
a.merge(bo, false);
assertTrue("Object does not override Array", Array.isArray(a[1]));

a = [];
a.merge(bo);
a.merge(b);
assertTrue("Array overrides Object", Array.isArray(a[1]));

a = [];
a.merge(bo);
a.merge(b, false);
assertTrue("Array does not override Object", !Array.isArray(a[1]));

Мой метод равно можно найти здесь: Сравнение объектов в JavaScript

10
23.05.2017 12:26:35

В MooTools есть Object.merge () :

Object.merge(obj1, obj2);
10
25.10.2016 20:29:53

В Ext JS 4 это можно сделать следующим образом:

var mergedObject = Ext.Object.merge(object1, object2)

// Or shorter:
var mergedObject2 = Ext.merge(object1, object2)

Смотрите слияние (объект): объект .

9
25.10.2016 20:40:00
Остерегайтесь ошибки EXTJS-9173 в EXT.Object.merge, так как +2 года до сих пор не устранены
Chris 5.08.2015 14:35:43

Ого ... это первый пост StackOverflow, который я видел на нескольких страницах. Извиняюсь за добавление еще одного "ответа"

Этот метод предназначен для ES5 и более ранних версий - существует множество других ответов на ES6.

Я не видел никакого "глубокого" слияния объекта с использованием argumentsсвойства. Вот мой ответ - компактный и рекурсивный , позволяющий передавать неограниченные аргументы объекта:

function extend() {
    for (var o = {}, i = 0; i < arguments.length; i++) {
        // if (arguments[i].constructor !== Object) continue;
        for (var k in arguments[i]) {
            if (arguments[i].hasOwnProperty(k)) {
                o[k] = arguments[i][k].constructor === Object ? extend(o[k] || {}, arguments[i][k]) : arguments[i][k];
            }
        }
    }
    return o;
}

Закомментированная часть является необязательной. Она просто пропускает передаваемые аргументы, которые не являются объектами (предотвращая ошибки).

Пример:

extend({
    api: 1,
    params: {
        query: 'hello'
    }
}, {
    params: {
        query: 'there'
    }
});

// outputs {api: 1, params: {query: 'there'}}

Этот ответ сейчас капля в море ...

9
1.05.2018 18:44:14
Самый короткий ответ, который прекрасно работает! Это заслуживает большего количества голосов!
Jens Törnell 11.02.2020 07:09:30
var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }

// result
result: {food: "pizza", car: "ford", animal: "dog"}

Использование jQuery.extend () - Ссылка

// Merge obj1 & obj2 to result
var result1 = $.extend( {}, obj1, obj2 );

Использование _.merge () - Ссылка

// Merge obj1 & obj2 to result
var result2 = _.merge( {}, obj1, obj2 );

Использование _.extend () - Ссылка

// Merge obj1 & obj2 to result
var result3 = _.extend( {}, obj1, obj2 );

Использование Object.assign () ECMAScript 2015 (ES6) - Ссылка

// Merge obj1 & obj2 to result
var result4 = Object.assign( {}, obj1, obj2 );

Выход всего

obj1: { animal: 'dog' }
obj2: { food: 'pizza', car: 'ford' }
result1: {food: "pizza", car: "ford", animal: "dog"}
result2: {food: "pizza", car: "ford", animal: "dog"}
result3: {food: "pizza", car: "ford", animal: "dog"}
result4: {food: "pizza", car: "ford", animal: "dog"}
8
18.09.2018 01:55:43

Основываясь на ответах Markus и vsync , это расширенная версия. Функция принимает любое количество аргументов. Его можно использовать для установки свойств на узлах DOM и создания глубоких копий значений. Тем не менее, первый аргумент дается ссылкой.

Для обнаружения узла DOM используется функция isDOMNode () (см. Вопрос переполнения стека JavaScript isDOM - Как проверить, является ли объект JavaScript объектом DOM? )

Он был протестирован в Opera 11, Firefox 6, Internet Explorer 8 и Google Chrome 16.

Код

function mergeRecursive() {

  // _mergeRecursive does the actual job with two arguments.
  var _mergeRecursive = function (dst, src) {
    if (isDOMNode(src) || typeof src !== 'object' || src === null) {
      return dst;
    }

    for (var p in src) {
      if (!src.hasOwnProperty(p))
        continue;
      if (src[p] === undefined)
        continue;
      if ( typeof src[p] !== 'object' || src[p] === null) {
        dst[p] = src[p];
      } else if (typeof dst[p]!=='object' || dst[p] === null) {
        dst[p] = _mergeRecursive(src[p].constructor===Array ? [] : {}, src[p]);
      } else {
        _mergeRecursive(dst[p], src[p]);
      }
    }
    return dst;
  }

  // Loop through arguments and merge them into the first argument.
  var out = arguments[0];
  if (typeof out !== 'object' || out === null)
    return out;
  for (var i = 1, il = arguments.length; i < il; i++) {
    _mergeRecursive(out, arguments[i]);
  }
  return out;
}

Некоторые примеры

Установить innerHTML и стиль элемента HTML

mergeRecursive(
  document.getElementById('mydiv'),
  {style: {border: '5px solid green', color: 'red'}},
  {innerHTML: 'Hello world!'});

Слияние массивов и объектов. Обратите внимание, что undefined может использоваться для сохранения значений в левом массиве / объекте.

o = mergeRecursive({a:'a'}, [1,2,3], [undefined, null, [30,31]], {a:undefined, b:'b'});
// o = {0:1, 1:null, 2:[30,31], a:'a', b:'b'}

Любой аргумент, не являющийся объектом JavaScript (включая ноль), будет игнорироваться. За исключением первого аргумента, также DOM-узлы отбрасываются. Помните, что строки, созданные как new String (), на самом деле являются объектами.

o = mergeRecursive({a:'a'}, 1, true, null, undefined, [1,2,3], 'bc', new String('de'));
// o = {0:'d', 1:'e', 2:3, a:'a'}

Если вы хотите объединить два объекта в новый (не затрагивая ни один из двух), укажите {} в качестве первого аргумента

var a={}, b={b:'abc'}, c={c:'cde'}, o;
o = mergeRecursive(a, b, c);
// o===a is true, o===b is false, o===c is false

Изменить (от ReaperSoon):

Также объединить массивы

function mergeRecursive(obj1, obj2) {
  if (Array.isArray(obj2)) { return obj1.concat(obj2); }
  for (var p in obj2) {
    try {
      // Property in destination object set; update its value.
      if ( obj2[p].constructor==Object ) {
        obj1[p] = mergeRecursive(obj1[p], obj2[p]);
      } else if (Array.isArray(obj2[p])) {
        obj1[p] = obj1[p].concat(obj2[p]);
      } else {
        obj1[p] = obj2[p];
      }
    } catch(e) {
      // Property in destination object not set; create it and set its value.
      obj1[p] = obj2[p];
    }
  }
  return obj1;
}
7
6.11.2017 21:27:02

let obj1 = {a:1, b:2};
let obj2 = {c:3, d:4};
let merged = {...obj1, ...obj2};
console.log(merged);

6
17.01.2019 11:30:02

Вы должны использовать lodash по умолчаниюDeep

_.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } });
// → { 'user': { 'name': 'barney', 'age': 36 } }
5
22.12.2015 11:02:49

С Underscore.js , чтобы объединить массив объектов, сделайте:

var arrayOfObjects = [ {a:1}, {b:2, c:3}, {d:4} ];
_(arrayOfObjects).reduce(function(memo, o) { return _(memo).extend(o); });

Это приводит к:

Object {a: 1, b: 2, c: 3, d: 4}
5
25.10.2016 21:53:24