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

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

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

obj1.merge(obj2);

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

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

Стоит отметить этот ответ на аналогичный вопрос, который показывает, как объединить «на один уровень ниже». То есть он объединяет значения повторяющихся ключей (вместо перезаписи первого значения вторым значением), но не выполняет рекурсию дальше этого. IMHO, это хороший чистый код для этой задачи.

ToolmakerSteve 04.10.2019 00:42

Кстати, несколько верхних ответов выполняют «неглубокое» слияние: если один и тот же ключ существует как в obj1, так и в obj2, значение в obj2 сохраняется, значение в obj1 отбрасывается. Например. если в примере вопроса был var obj2 = { animal: 'dog', food: 'bone' };, слияние будет { food: 'bone', car: 'ford', animal: 'dog' }. Если вы работаете с «вложенными данными» и хотите «глубокое слияние», поищите ответы, в которых упоминается «глубокое слияние» или «рекурсия». Если у вас есть значения arrays, используйте параметр arrayMerge в github «TehShrike / deepmerge», как упоминалось в здесь.

ToolmakerSteve 04.10.2019 15:30

Пожалуйста, перейдите по ссылке ниже stackoverflow.com/questions/171251/… Оператор спреда, который является новой функцией в версии ES6

Soura Sankar Ghosh 11.01.2020 16:00
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2 731
3
1 450 654
66
Перейти к ответу Данный вопрос помечен как решенный

Ответы 66

Ответ принят как подходящий

Стандартный метод 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, вам понадобится плагин Бабель-плагин-преобразование-объект-отдых-распространение для его работы.

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)


Метод для 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;
}

Это не работает, если объекты имеют атрибуты с одинаковыми именами, и вы также можете объединить атрибуты.

Xiè Jìléi 24.10.2010 14:56

Это только поверхностное копирование / слияние. Имеет потенциал уничтожить множество элементов.

Jay Taylor 02.06.2011 19:39

+1 за признание того, что некоторые бедняги вынуждены использовать фреймворки, которые гадят на всех их прототипах ...

thejonwithnoh 07.05.2016 07:18

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

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

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

Ты имел ввиду Object.prototype.extend? В любом случае расширять Object.prototype - не лучшая идея. -1.

SolutionYogi 01.08.2009 00:08

Если подумать, это, вероятно, должен быть Object.prototype, а не Object ... Хорошая идея или нет, это то, что делает фреймворк prototype.js, и поэтому, если вы используете его или другой фреймворк, который зависит от него, Object.prototype уже будет расширен. с extend.

ephemient 01.08.2009 01:14

Я не хочу вас кричать, но сказать, что это нормально, потому что prototype.js делает это, - плохой аргумент. Prototype.js популярен, но это не значит, что они являются образцом для подражания в том, как создавать JavaScript. Ознакомьтесь с подробным обзором библиотеки Prototype, написанным профессионалом JS. dhtmlkitchen.com/?category=/JavaScript/&date=2008/06/17/…

SolutionYogi 01.08.2009 01:19

Я не имею в виду, что это нормально - я имею в виду, что он может уже быть там, поэтому вам может не понадобиться собственный extend.

ephemient 01.08.2009 01:49

Какая разница, правильно это или неправильно? Если это работает, значит, работает. Ожидание идеального решения - это здорово, кроме случаев, когда вы пытаетесь сохранить контракт, привлечь новых клиентов или уложиться в сроки. Дайте здесь всем, кто увлечен программированием, бесплатные деньги, и они в конечном итоге все сделают «правильно».

David 19.08.2009 08:24

Дэвид, потому что вы хотите, чтобы ваш сайт продолжал работать при изменении браузеров. Решение Йоги, я не думаю, что что-то не так с тем, как Prototype добавляет вещи в Object.prototype. Возможность добавлять новые методы путем изменения constructor.prototype - это тот же способ, которым JS выполняет объектно-ориентированный подход. Он просто немного отличается от других объектно-ориентированных языков, с которыми вы, возможно, работали. Одним из исключений является Ruby, который также позволяет вам изменять существующие экземпляры и классы с помощью расширения и включения (включить в Ruby ~ constructor.prototype.extend в JS).

allyourcode 14.12.2009 03:37

В документации Mozilla указано, что Object.prototype предоставляется именно для этой цели: developer.mozilla.org/en/Core_JavaScript_1.5_Reference/…

allyourcode 14.12.2009 03:49

Где вы все нашли Object.prototype? Object.extend не будет мешать вашим объектам, он просто присоединяет функцию к объекту Object. И obj1.extend(obj2) не является правильным способом активации функции, используйте Object.extend(obj1, obj2) insted

artnikpro 03.12.2013 17:23

@allyourcode - я не читаю это в документации - хотя, конечно, они могли измениться после вашего комментария. AFAIK, основное использование Object.prototype состоит в том, чтобы иметь стандартный набор методов / свойств, которые известны всем объектам, и отвергать, если необходимо, в конструкторах новых объектов (которые, таким образом, эквивалентны «подклассам» в OO). Важно отметить, что такой отвергать не изменяет сам Object.prototype; он изменяет поведение вашего нового «подкласса». В документации ничего не говорится о том, хорошо или плохо добавление настраиваемых свойств в Object.prototype.

ToolmakerSteve 04.10.2019 14:34

повторно комментарий "добавить в Object.prototype". "обезьяна-патч" - Документы уточняют, что нет следует расширять Object.Prototype. В developer.mozilla.org/en-US/docs/Web/JavaScript/… «Плохая практика: расширение собственных прототипов. Одна из ошибок, которая часто используется, - это расширение Object.prototype или одного из других встроенных прототипов. Этот метод называется исправлением обезьяны и нарушает инкапсуляцию. Хотя он используется популярными фреймворками, такими как Prototype.js, по-прежнему нет веских причин загромождать встроенные типы дополнительными нестандартными функциями ".

ToolmakerSteve 05.10.2019 15:20

В 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.

Осторожно: переменная "settings" все же будет изменена. jQuery не возвращает новый экземпляр. Причина этого (и именования) в том, что .extend () был разработан для расширения объектов, а не для объединения вещей вместе. Если вам нужен новый объект (например, настройки по умолчанию, которые вы не хотите трогать), вы всегда можете jQuery.extend ({}, settings, options);

webmat 04.05.2011 20:01

Имейте в виду, что jQuery.extend также имеет глубокую (логическую) настройку. jQuery.extend(true,settings,override), что важно, если свойство в настройках содержит объект, а переопределение имеет только часть этого объекта. Вместо удаления несогласованных свойств глубокая настройка будет обновляться только там, где она существует. По умолчанию - false.

vol7ron 10.06.2011 01:09

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

(Теперь код не использует 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 как

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

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

skerit 16.01.2011 19:00

Это было то, что я искал. Обязательно проверьте, есть ли obj2.hasOwnProperty (p), прежде чем переходить к оператору try catch. В противном случае вы можете столкнуться с каким-то другим дерьмом, которое будет сливаться с более высоких уровней цепочки прототипов.

Adam 09.07.2011 06:25

Спасибо, Маркус. Обратите внимание, что o1 также изменяется с помощью MergeRecursive, поэтому в конце o1 и o3 идентичны. Это может быть более интуитивно понятным, если вы ничего не возвращаете, чтобы пользователь знал, что то, что они предоставляют (o1), изменяется и становится результатом. Кроме того, в вашем примере, возможно, стоит добавить в o2 что-то, чего изначально не было в o1, чтобы показать, что свойства o2 вставляются в o1 (кто-то другой ниже представил альтернативный код, копирующий ваш пример, но их код ломается, когда o2 содержит свойства, не в o1 - но ваш на этот счет работает).

pancake 24.04.2012 09:40

Я не уверен, чем это отличается от stackoverflow.com/a/171256/351426. Используя тот же ввод, он дает тот же результат. Что в твоем делает его более «выздоравливающим»?

Jono 21.02.2013 02:37

@JonoRR - вызов самого себя делает его рекурсивным: obj1 [p] = MergeRecursive (obj1 [p], obj2 [p]);

z8000 17.04.2013 23:25

Это хороший ответ, но пара придирок: 1) Никакая логика не должна основываться на исключениях; в этом случае любое выброшенное исключение будет перехвачено с непредсказуемыми результатами. 2) Я согласен с комментарием @ pancake о том, что ничего не возвращать, поскольку исходный объект изменен. Вот пример jsFiddle без логики исключения: jsfiddle.net/jlowery2663/z8at6knn/4 - Джефф Лоури

Jeff Lowery 26.11.2014 00:14

@JeffLowery Хороший пример скрипки: if (obj2 [p]! == null && obj2 [p] .constructor == Object) {пропускает нулевые значения для

Chris 06.08.2015 07:33

Использование попробуй поймать совершенно бесплатно, и obj2[p].constructor==Object во многих случаях выйдет из строя.

RobG 28.09.2015 06:35

Кроме того, obj2 [p] .constructor == Array необходим в любом случае, когда есть массив объектов.

The Composer 15.12.2015 22:27

Чтобы включить глубокие копии объектов пустой, измените if ( obj2[p].constructor==Object ) { на if ( obj2[p].constructor==Object && (Object.keys(obj2[p]).length > 0) ) {. Однако это значительно замедлит его.

GreenRaccoon23 09.11.2016 22:37

@ GreenRaccoon23 Если obj2 [p] пуст, значит, у него нет свойств, поэтому MergeRecursive(obj1[p], obj2[p]) оставит obj1[p] без изменений - как и должно. «глубокая копия пустого объекта» не имеет смысла - нет свойств, которые можно объединить в 1 - приведите пример того, что вы пытаетесь сделать, чего не делает существующий код.

ToolmakerSteve 03.10.2019 23:58

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

Правильная реализация в Прототип должна выглядеть так:

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

obj1 = Object.extend(obj1, obj2);

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

Object.merge(obj1, obj2);

Для не слишком сложных объектов вы можете использовать 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_/

Имейте в виду, что в этом примере "} {" не должно происходить внутри строки!

var so1 = JSON.stringify(obj1); var so2 = JSON.stringify(obj1); objMerge = so1.substr(0, so1.length-1)+","+so2.substr(1); console.info(objMerge);

Quamis 03.09.2013 14:55

function merge(obj1, obj2) { return JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)) .replace(/\}\{/g, ',').replace(/,\}/g, '}').replace(/\{,/g, '{')); }

Alex Ivasyuv 04.03.2015 17:54

или как однострочник: objMerge = JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)).replace(/\}\{/, ", "));

Rabbi Shuki Gur 20.01.2017 10:06

@Charles, извините, но как "этот метод очень опасен", но также забавен - это самый безопасный из возможных. Во-первых, он использует JSON для выполнения тяжелой работы по чтению содержимого объекта, во-вторых, он использует объект JSON, который, безусловно, является самым безопасным машина оптимизирована для повторного анализа JSO из строкового литерала совместимого объекта JSON. А также обратно совместим с IE8, который является первым браузером, реализующим его стандарт. Это просто заставляет меня задаться вопросом: «Почему, ну, почему ты сказал такую ​​глупость и ушел с рук»?

Bekim Bacaj 15.04.2017 18:49

Я считаю, что функции объекта здесь не работают, поскольку нельзя использовать stringify, но можем ли мы прояснить другие недостатки?

yeahdixon 25.03.2018 07:23

Как правило, не использует решения, которые полагаются на разбор, когда есть известные решения, которые не нуждаются в синтаксическом анализе. В этом случае требование, чтобы "} {" не встречалось в строке, кажется "достаточно безобидным", но это может случиться, так что это не лучший метод, на который можно полагаться. И, конечно, он плохо масштабируется (производительность, память) для глубоко вложенных объектов, учитывая, что вы конвертируете в строку и обратно. ИМХО, только не делайте этого.

ToolmakerSteve 04.10.2019 13:54

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

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

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

Object.defineProperty(Object.prototype, "extend", {
    enumerable: false,
    value: function(from) {
        var props = Object.getOwnPropertyNames(from);
        var dest = this;
        props.forEach(function(name) {
            if (name in dest) {
                var destination = Object.getOwnPropertyDescriptor(from, name);
                Object.defineProperty(dest, name, destination);
            }
        });
        return this;
    }
});

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

var obj = {
    name: 'stack',
    finish: 'overflow'
}
var replacement = {
    name: 'stock'
};

obj.extend(replacement);

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

Подождите, код не работает! :( вставил его в jsperf для сравнения с другими алгоритмами слияния, и код не работает - функция extension не существует

Jens Roland 13.12.2010 17:57

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

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

Если переопределение ложно, никакое свойство не переопределяется, но новые свойства будут добавлены.

Использование: 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]));

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

Я как бы начинаю работать с JavaScript, поправьте меня, если я ошибаюсь.

Но не лучше ли было бы объединить любое количество объектов? Вот как я это делаю, используя собственный объект Arguments.

Ключ к этому в том, что вы можете передать любое количество аргументов функции JavaScript, не определяя их в объявлении функции. Вы просто не можете получить к ним доступ без использования объекта Arguments.

function mergeObjects() (
    var tmpObj = {};

    for(var o in arguments) {
        for(var m in arguments[o]) {
            tmpObj[m] = arguments[o][m];
        }
    }
    return tmpObj;
}

Использовать:

//Takes any number of objects and returns one merged object
var objectMerge = function(){
    var out = {};
    if (!arguments.length)
        return out;
    for(var i=0; i<arguments.length; i++) {
        for(var key in arguments[i]){
            out[key] = arguments[i][key];
        }
    }
    return out;
}

Он был протестирован с:

console.info(objectMerge({a:1, b:2}, {a:2, c:4}));

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

{ a: 2, b: 2, c: 4 }

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

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

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

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

Остерегайтесь ошибки EXTJS-9173 в EXT.Object.merge, которая еще не устранена через 2 года.

Chris 05.08.2015 17:35

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

Отметьте эти две строки:

from = arguments[i];
Object.getOwnPropertyNames(from).forEach(function (name) {

Необходимо проверить "от" на нулевой объект ... Если, например, слияние объекта, полученного из ответа Аякс, ранее созданного на сервере, свойство объекта может иметь значение "null", и в этом случае указанное выше код генерирует ошибку:

"from" is not a valid object

Так, например, добавление функции «... Object.getOwnPropertyNames (from) .forEach ...» в оболочку «if (from! = Null) {...}» предотвратит возникновение этой ошибки.

function extend(o, o1, o2){
    if ( !(o instanceof Object) ) o = {};

    copy(o, o1);
    if ( o2 )
        copy(o, o2)

    function isObject(obj) {
        var type = Object.prototype.toString.call(obj);
        return obj === Object(obj) && type != '[object Array]' && type != '[object Function]';
    };

    function copy(a,b){
        // copy o2 to o
        for( var key in b )
            if ( b.hasOwnProperty(key) ){
                if ( isObject(b[key]) ){
                    if ( !isObject(a[key]) )
                        a[key] = Object.assign({}, b[key]); 
                    else copy(a[key], b[key])
                }
                else
                    a[key] = b[key];
            }
    }

    return o;
};


var o1 = {a:{foo:1}, b:1},
    o2 = {a:{bar:2}, b:[1], c:()=>{}},
    newMerged = extend({}, o1, o2);
    
console.info( newMerged )
console.info( o1 )
console.info( o2 )

Это не удается, если o2 содержит какие-либо свойства, которых еще нет в o1, именно поэтому у Маркуса есть удаленная вами попытка / уловка. Ваш пример работает только потому, что все свойства o2 уже существуют в o1. Итак, это не слияние, и это не лучше!

pancake 24.04.2012 09:02

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

vsync 03.09.2013 16:16

Следует отметить, что функция является деструктивной и obj1а также любые объекты, вложенные в нее, будут изменены, что может быть или не быть тем, что вы хотели. Для ясности я сам переименовал функцию и аргументы в applyProperties(target, source).

mindplay.dk 04.04.2016 18:51

@ mindplay.dk - что вы имеете в виду разрушительный? Дело в том, чтобы объединить o2 с o1, а не создавать новый (третий) объект o1, объединенный с o2. Я ответил на точный вопрос

vsync 04.04.2016 21:06

@vsync деструктивен в том смысле, что, например, Array.sort () деструктивен, а Array.slice () - нет - это не критика, просто указание на факт :-)

mindplay.dk 09.04.2016 17:48

@ mindplay.dk - в вопросе был задан явно, чтобы объединить один объект с другим, переопределив первый, поэтому очевидно, что это деструктивно по природе рассматриваемого вопроса. для неразрушающего ответа нужно перейти на страницу другого ответа

vsync 09.04.2016 19:01

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

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.info(merge(t1, t2));
console.info(merge(t1, t3));
console.info(merge(t2, t3));
console.info(merge(t1, t2, t3));
console.info(merge({}, t1, { key1: 1 }));

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

Strikers 20.02.2015 14:41

Но разве это не ожидаемое поведение? Цель этой функции - переопределить значения в порядке аргументов. Следующий будет иметь приоритет над текущим. Как сохранить уникальные ценности? Из моего примера, чего вы ждете от merge({}, t1, { key1: 1 })?

Emre Erkan 20.02.2015 14:50

Я ожидаю объединить только значения, не являющиеся объектно-типизированными.

AGamePlayer 28.02.2015 17:48

не выполняется, если ожидаемый результат выглядит следующим образом: gist.github.com/ceremcem/a54f90923c6e6ec42c7daf24cf2768bd

ceremcem 01.08.2016 04:21

Это сработало для меня в строгом режиме для моего проекта node.js. Пока что это лучший ответ на этот пост.

klewis 16.03.2018 23:05

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

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

Он был протестирован в Опера 11, Firefox 6, Интернет-технологии и 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 (включая null), будет проигнорирован. За исключением первого аргумента, также отбрасываются узлы 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;
}

В ЮйY.merge должен выполнить свою работу:

Y.merge(obj1, obj2, obj3....) 

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

_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}

Этот пример хорош, поскольку вы имеете дело с анонимными объектами, но если это не так, тогда здесь применяется предупреждение webmat комментарий в ответе jQuery о мутациях, как подчеркивание также изменяет целевой объект. Как и ответ jQuery, чтобы сделать это без мутаций, просто слейтесь с пустым объектом: _({}).extend(obj1, obj2);

Abe Voelker 05.06.2012 22:12

Есть еще один, который может иметь значение в зависимости от того, чего вы пытаетесь достичь: _.defaults(object, *defaults) «Заполните пустые и неопределенные свойства в объекте значениями из объектов по умолчанию и верните объект».

conny 10.12.2012 20:08

Многие люди прокомментировали изменение целевого объекта, но вместо того, чтобы создать новый метод методом, общим шаблоном (например, в dojo) является использование dojo.mixin (dojo.clone (myobj), {newpropertys: "для добавления to myobj "})

Colin D 26.03.2016 05:57

Подчеркивание может принимать произвольное количество объектов, поэтому вы можете избежать мутации исходного объекта, выполнив var mergedObj = _.extend({}, obj1, obj2).

lobati 26.04.2016 05:49

Просто если кто использует Библиотека закрытия Google:

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'

function extend()
{ 
    var o = {}; 

    for (var i in arguments)
    { 
        var s = arguments[i]; 

        for (var i in s)
        { 
            o[i] = s[i]; 
        } 
    } 

    return o;
}

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

Вот как действительно слились области объектов 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);

Мне очень помогло, расширение js с подчеркиванием действительно перезаписало, а не слило

David Cumps 26.02.2014 12:01

Стоит отметить, что typeof array и typeof null вернут 'object' и нарушат это.

Salakar 19.07.2016 15:18

@ u01jmg3 см. мой ответ здесь: stackoverflow.com/questions/27936772/… - функция isObject

Salakar 19.04.2017 15:31

Это не работает, если вы используете строгий режим для проектов node.js

klewis 16.03.2018 23:01

Это объединяет obj с def "по умолчанию". obj имеет приоритет для всего, что существует в обоих, поскольку obj копируется в def. Также обратите внимание, что это рекурсивно.

function mergeObjs(def, obj) {
    if (typeof obj == 'undefined') {
        return def;
    } else if (typeof def == 'undefined') {
        return obj;
    }
    for (var i in obj) {
        if (obj[i] != null && obj[i].constructor == Object) {
            def[i] = mergeObjs(def[i], obj[i]);
        } else {
            def[i] = obj[i];
        }
    }
    return def;
}

a = {x : {y : [123]}}
b = {x : {z : 123}}
console.info(mergeObjs(a, b));
// {x: {y : [123], z : 123}}

Стоит отметить, что версия из Коллекция 140byt.es решает задачу в минимальном объеме, и для этого стоит попробовать:

Код:

function m(a,b,c){for(c in b)b.hasOwnProperty(c)&&((typeof a[c])[0]=='o'?m(a[c],b[c]):a[c]=b[c])}

Использование для ваших целей:

m(obj1,obj2);

Вот оригинальный Gist.

A = {a:1,b:function(){alert(9)}}
B = {a:2,c:3}
A.merge = function(){for(var i in B){A[i]=B[i]}}
A.merge()

Результат: {a: 2, c: 3, b: function ()}

кстати, в dojo есть функция под названием mixin, поэтому dojo.mixin (A, B) сделает свое дело

jleviaguirre 19.12.2012 21:41

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

  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
    };
*/

По сравнению с другими, которые я тестировал на этой странице, эта функция действительно рекурсивна (выполняет глубокое слияние) и имитирует то, что действительно хорошо делает jQuery.extend (). Однако было бы лучше, если бы он изменил первый объект / аргумент, чтобы пользователь мог решить, хочет ли он передать пустой объект {} в качестве первого параметра или заставить функцию изменить исходный объект. Поэтому, если вы измените dst = {} на dst = arguments[0], он изменит первый объект, который вы передаете объединенному объекту.

Jasdeep Khalsa 27.03.2015 17:48

Теперь перестал работать в хроме. Я считаю плохую идею использовать такую ​​конструкцию toString.call(src) == '[object Object]' для проверки наличия объекта или его отсутствия. typeof src намного лучше. Более того, он преобразует массивы в объект (по крайней мере, у меня такое поведение на последней версии Chrome).

m03geek 21.05.2015 17:51

Мне нужно было сделать это внутри жадного цикла, я протестировал эту функцию по сравнению с той, которую я использовал до сих пор _.merge (object, [sources]) из lodash (своего рода библиотека подчеркивания): ваша функция почти в два раза быстрее Спасибо друг.

Arnaud Bouchot 11.10.2016 17:13

Вы можете назначить каждому объекту метод слияния по умолчанию (возможно, `` унаследовать '' лучшее имя):

Он должен работать либо с объектами, либо с конкретными функциями.

Приведенный ниже код обрабатывает переопределение объединенных значений, если это необходимо:

Object.prototype.merge = function(obj, override) {
// Don't override by default

    for (var key in obj) {
        var n = obj[key];
        var t = this[key];
        this[key] = (override && t) ? n : t;
    };

};

Данные испытаний ниже:

var Mammal = function () {
    this.eyes = 2;
    this.thinking_brain = false;
    this.say = function () {
    console.info('screaming like a mammal')};
}

var Human = function () {
    this.thinking_brain = true;
    this.say = function() {console.info('shouting like a human')};
}

john = new Human();

// Extend mammal, but do not override from mammal
john.merge(new Mammal());
john.say();

// Extend mammal and override from mammal
john.merge(new Mammal(), true);
john.say();

Мой метод:

function mergeObjects(defaults, settings) {
    Object.keys(defaults).forEach(function(key_default) {
        if (typeof settings[key_default] == "undefined") {
            settings[key_default] = defaults[key_default];
        } else if (isObject(defaults[key_default]) && isObject(settings[key_default])) {
            mergeObjects(defaults[key_default], settings[key_default]);
        }
    });

    function isObject(object) {
        return Object.prototype.toString.call(object) === '[object Object]';
    }

    return settings;
}

:)

Я использовал Object.create (), чтобы сохранить настройки по умолчанию (используя __proto__ или Object.getPrototypeOf ()).

function myPlugin( settings ){
    var defaults = {
        "keyName": [ "string 1", "string 2" ]
    }
    var options = Object.create( defaults );
    for (var key in settings) { options[key] = settings[key]; }
}
myPlugin( { "keyName": ["string 3", "string 4" ] } );

Таким образом, я всегда могу позже выполнить «concat ()» или «push ()».

var newArray = options['keyName'].concat( options.__proto__['keyName'] );

Примечание: вам нужно будет выполнить проверку hasOwnProperty перед объединением, чтобы избежать дублирования.

Подобно 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);

@naXa Я думаю, это был бы вариант, если вы не хотите добавлять библиотеку только для этого fn.

juanmf 22.03.2016 16:10

С помощью 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}

Для тех, кто использует Node.js, есть модуль NPM: node.extend

Установить:

npm install node.extend

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

var extend = require('node.extend');
var destObject = extend(true, {}, sourceObject);
// Where sourceObject is the object whose properties will be copied into another.

Это не встроенная библиотека, это модуль NPM.

ItalyPaleAle 30.04.2014 00:34

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

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

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

Object.assign(obj1, obj2);

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

Обратите внимание, что это только неглубокое слияние

Joachim Lous 16.03.2016 13:04

Я думаю, что пока что у Object.assign довольно приличное покрытие: kangax.github.io/compat-table/es6/…

brass monkey 26.05.2016 18:38

Теперь это должен быть правильный ответ. В настоящее время люди компилируют свой код для совместимости с редким браузером (IE11), который не поддерживает такого рода вещи. Боковое примечание: если вы не хотите добавлять obj2 в obj1, вы можете вернуть новый объект, используя Object.assign({}, obj1, obj2)

Duvrai 21.07.2016 23:02

@Duvrai Согласно отчетам, которые я видел, IE11 точно не редкость составлял около 18% доли рынка по состоянию на июль 2016 года.

NanoWizard 08.08.2016 22:15

Это решение создает новый объект и может обрабатывать несколько объектов.

Кроме того, это рекурсивный, и вы можете выбрать погоду от хочу до перезаписать значения и Объекты.

    function extendObjects() {

        var newObject        = {};
        var overwriteValues  = false;
        var overwriteObjects = false;

        for ( var indexArgument = 0; indexArgument < arguments.length; indexArgument++ ) {

            if ( typeof arguments[indexArgument] !== 'object' ) {

                if ( arguments[indexArgument] == 'overwriteValues_True' ) {

                    overwriteValues = true;            
                } else if ( arguments[indexArgument] == 'overwriteValues_False' ) {

                    overwriteValues = false;                             
                } else if ( arguments[indexArgument] == 'overwriteObjects_True' ) {

                    overwriteObjects = true;     
                } else if ( arguments[indexArgument] == 'overwriteObjects_False' ) {

                    overwriteObjects = false; 
                }

            } else {

                extendObject( arguments[indexArgument], newObject, overwriteValues, overwriteObjects );
            }

        }

        function extendObject( object, extendedObject, overwriteValues, overwriteObjects ) {

            for ( var indexObject in object ) {

                if ( typeof object[indexObject] === 'object' ) {

                    if ( typeof extendedObject[indexObject] === "undefined" || overwriteObjects ) {
                        extendedObject[indexObject] = object[indexObject];
                    }

                    extendObject( object[indexObject], extendedObject[indexObject], overwriteValues, overwriteObjects );

                } else {

                    if ( typeof extendedObject[indexObject] === "undefined" || overwriteValues ) {
                        extendedObject[indexObject] = object[indexObject];
                    }

                }

            }     

            return extendedObject;

        }

        return newObject;
    }

    var object1           = { a : 1, b : 2, testArr : [888, { innArr : 1 }, 777 ], data : { e : 12, c : { lol : 1 }, rofl : { O : 3 } } };
    var object2           = { a : 6, b : 9, data : { a : 17, b : 18, e : 13, rofl : { O : 99, copter : { mao : 1 } } }, hexa : { tetra : 66 } };
    var object3           = { f : 13, g : 666, a : 333, data : { c : { xD : 45 } }, testArr : [888, { innArr : 3 }, 555 ]  };

    var newExtendedObject = extendObjects( 'overwriteValues_False', 'overwriteObjects_False', object1, object2, object3 );

Содержимое newExtendedObject:

{"a":1,"b":2,"testArr":[888,{"innArr":1},777],"data":{"e":12,"c":{"lol":1,"xD":45},"rofl":{"O":3,"copter":{"mao":1}},"a":17,"b":18},"hexa":{"tetra":66},"f":13,"g":666}

Рабочий пример: http://jsfiddle.net/o0gb2umb/

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

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

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

Wtower 01.09.2016 16:39

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

SamBrick 01.09.2017 12:44

правда @Wtower. Просто из-за этого был баг. Лучше всего убедиться, что он всегда начинается как _.merge ({}, ...

Martin Cremer 10.10.2019 13:09

Другой способ:

function concat_collection(obj1, obj2) {
    var i;
    var arr = new Array();

    var len1 = obj1.length;
    for (i=0; i<len1; i++) {
        arr.push(obj1[i]);
    }

    var len2 = obj2.length;
    for (i=0; i<len2; i++) {
        arr.push(obj2[i]);
    }

    return arr;
}

var ELEMENTS = concat_collection(A,B);
for(var i = 0; i < ELEMENTS.length; i++) {
    alert(ELEMENTS[i].value);
}

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

var merge = function() {
  var il = arguments.length;

  for (var i = il - 1; i > 0; --i) {
    for (var key in arguments[i]) {
      if (arguments[i].hasOwnProperty(key)) {
        arguments[0][key] = arguments[i][key];
      }
    }
  }
};

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

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

jQuery.extend(obj1, obj2);

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

obj1 будет содержать все значения, а не obj2. Вы расширяете первый объект. jQuery.extend( target [, object1 ] [, objectN ] )

ruuter 21.10.2015 22:33

Этот вопрос не о jQuery. JavaScript! = JQuery

Jorge Riv 15.04.2016 00:38

Я искал в Google JavaScript, но это более простой подход

Ehsan 28.06.2016 10:00

Если вы используете Набор инструментов Dojo, то лучший способ объединить два объекта - использовать миксин.

Ниже приведен пример миксина Dojo Toolkit:

// Dojo 1.7+ (AMD)
require(["dojo/_base/lang"], function(lang){
  var a = { b:"c", d:"e" };
  lang.mixin(a, { d:"f", g:"h" });
  console.info(a); // b:c, d:f, g:h
});

// Dojo < 1.7
var a = { b:"c", d:"e" };
dojo.mixin(a, { d:"f", g:"h" });
console.info(a); // b:c, d:f, g:h

Для более подробной информации, пожалуйста, миксин.

Вы можете объединять объекты, следуя моему методу

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

var result = mergeObjects([obj1, obj2]);

console.info(result);
document.write("result: <pre>" + JSON.stringify(result, 0, 3) + "</pre>");

function mergeObjects(objectArray) {
    if (objectArray.length) {
        var b = "", i = -1;
        while (objectArray[++i]) {
            var str = JSON.stringify(objectArray[i]);
            b += str.slice(1, str.length - 1);
            if (objectArray[i + 1]) b += ",";
        }
        return JSON.parse("{" + b + "}");
    }
    return {};
}

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.info(obj); // { a: 1, b: 2, c: 3 }
console.info(o1);  // { a: 1, b: 2, c: 3 }, target object itself is changed.

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

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

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

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object.

Подробнее...

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

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;
    }
  });
}

что делает 'use strict' в старых браузерах или почему он там вообще есть. браузеры, поддерживающие объект [defineProperty; ключи; getOwnPropertyDescriptor; и т. д.], не совсем старые. Это просто более ранние версии UA поколения 5.

Bekim Bacaj 20.04.2017 22:39

Спасибо за пояснение, что Objects.assign возвращает объединенный объект, которого нет в других ответах. Это более функциональный стиль программирования.

Sridhar Sarnobat 09.05.2017 08:59

Вы должны использовать lodash defaultsDeep

_.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } });
// → { 'user': { 'name': 'barney', 'age': 36 } }

Возможный способ добиться этого заключается в следующем.

if (!Object.prototype.merge){
    Object.prototype.merge = function(obj){
        var self = this;
        Object.keys(obj).forEach(function(key){
            self[key] = obj[key]
        });
    }
};

Я не знаю, лучше ли это, чем другие ответы. В этом методе вы добавляете merge function к прототипу Objects. Таким образом вы можете вызвать obj1.merge(obj2);

Примечание: вы должны проверить свой аргумент, чтобы увидеть, является ли он объектом, и «выбросить» правильный Error. В противном случае Object.keys выдаст ошибку

Вот что я использовал в своей кодовой базе для слияния.

function merge(to, from) {
  if (typeof to === 'object' && typeof from === 'object') {
    for (var pro in from) {
      if (from.hasOwnProperty(pro)) {
        to[pro] = from[pro];
      }
    }
  }
  else{
      throw "Merge function can apply only on object";
  }
}

Если вам нужно глубокое слияние, которое также будет «объединять» массивы, объединяя их в результате, тогда эта функция ES6 может быть тем, что вам нужно:

function deepMerge(a, b) {
    // If neither is an object, return one of them:
    if (Object(a) !== a && Object(b) !== b) return b || a;
    // Replace remaining primitive by empty object/array
    if (Object(a) !== a) a = Array.isArray(b) ? [] : {};
    if (Object(b) !== b) b = Array.isArray(a) ? [] : {};
    // Treat arrays differently:
    if (Array.isArray(a) && Array.isArray(b)) {
        // Merging arrays is interpreted as concatenation of their deep clones:
        return [...a.map(v => deepMerge(v)), ...b.map(v => deepMerge(v))];
    } else {
        // Get the keys that exist in either object
        var keys = new Set([...Object.keys(a),...Object.keys(b)]);
        // Recurse and assign to new object
        return Object.assign({}, ...Array.from(keys,
            key => ({ [key]: deepMerge(a[key], b[key]) }) ));
    }
}

// Sample data for demo:
var a = {
    groups: [{
        group: [{
            name: 'John',
            age: 12
        },{
            name: 'Mary',
            age: 20
        }],
        groupName: 'Pair'
    }],
    config: {
        color: 'blue',
        range: 'far'
    }
};


var b = {
    groups: [{
        group: [{
            name: 'Bill',
            age: 15
        }],
        groupName: 'Loner'
    }],
    config: {
        range: 'close',
        strength: 'average'
    }
};

var merged = deepMerge(a, b);

console.info(merged);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Обратите внимание, что если этой функции передается только один аргумент, она действует как функция глубокого клонирования.

Для этого вы можете использовать синтаксис распространения объекта. Это часть ES2018 и не только.

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

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

Поддержка браузера?

Slava 11.10.2017 13:01

@ Alph.Dev Вы знаете caniuse.com?

connexo 02.01.2018 13:32

Я не могу заставить работать трехточечный синтаксис в node.js. узел жалуется на это.

klewis 16.03.2018 22:52

В EcmaScript2016 вы можете сделать следующее

Исправление: это предложение стадии 3, но оно всегда работало для меня.

const objA = {
  attrA: 'hello',
  attrB: true
}

const objB = {
  attrC: 2
}

const mergedObj = {...objA, ...objB}
Свойства распространения объекта are not a part of ES2016—it's a stage 3 proposal. Also, it was already covered in этот ответ.
Michał Perłakowski 02.02.2017 16:09

Слияние JSON-совместимых объектов JavaScript

Я рекомендую использовать неразрушающие методы, которые не изменяют исходный источник, Object.assign - это разрушительный метод, и это также не так, дружественный к производству, потому что он перестает работать в более ранних браузерах, и у вас нет возможности исправить его. чисто, с альтернативой.

Слияние объектов JS всегда будет недосягаемым или неполным, независимо от решения. Но объединение совместимых объектов, совместимых с JSON, находится всего в одном шаге от возможности написать простой и переносимый фрагмент кода неразрушающего метода слияния серии объектов JS в возвращаемый мастер, содержащий все уникальные имена свойств и их соответствующие значения, синтезированные в единый мастер-объект по прямому назначению.

Принимая во внимание, что MSIE8 является первым браузером, добавившим встроенную поддержку объекта JSON, это большое облегчение, а повторное использование уже существующей технологии всегда приветствуется.

Ограничение вашего кода стандартными объектами, совместимыми с JSON, является скорее преимуществом, чем ограничением, поскольку эти объекты также могут передаваться через Интернет. И, конечно же, для тех, кто хотел бы более глубокую обратную совместимость, всегда есть плагин json., Методы которого можно легко назначить переменной JSON во внешнем коде без необходимости изменять или переписывать используемый метод.

function Merge( ){
    var a = [].slice.call( arguments ), i = 0;
        while( a[i] )a[i] = JSON.stringify( a[i++] ).slice( 1,-1 );
        return JSON.parse( "{"+ a.join() +"}" );
    }

(Конечно, всегда можно дать ему более значимое имя, которое я еще не решил; вероятно, следует назвать его JSONmerge)

Вариант использования:

var master = Merge( obj1, obj2, obj3, ...objn );

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

Число аргументов слияния также ограничено Только пределом длины аргументов [который огромен]. Встроенная поддержка синтаксического анализа / строкового преобразования JSON уже оптимизирована для компьютера, что означает: он должен быть быстрее, чем любая скриптовая форма цикла JS. Итерация по заданным аргументам выполняется с использованием while, который оказался самым быстрым циклом в JS.

Не помешает кратко упомянуть тот факт, что мы уже знаем, что повторяющиеся свойства уникальных меток (ключей) объекта будут перезаписаны более поздним объектом, содержащим ту же метку ключа, что означает, что вы контролируете, какое свойство принимает на себя предыдущий, просто упорядочив или переупорядочив список аргументов. И преимущество получения чистого и обновленного мастер-объекта без дублирования в качестве окончательного результата.

;
var obj1 = {a:1}, obj2 = {b:2}, obj3 = {c:3}
;
function Merge( ){
    var a = [].slice.call( arguments ), i = 0;
        while( a[i] )a[i] = JSON.stringify( a[i++] ).slice( 1,-1 );
        return JSON.parse( "{"+ a.join() +"}" );
    }
;
var master = Merge( obj1, obj2, obj3 )
;
console.info( JSON.stringify( master ) )
;

ES5-совместимый родной однострочник:

var merged = [obj1, obj2].reduce(function(a, o) { for(k in o) a[k] = o[k]; return a; }, {})

** Слияние объектов просто с помощью оператора 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.info(mergedObj);

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

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

Вау ... это первая публикация на StackOverflow, которую я видел с несколькими страницами. Приносим извинения за добавление еще одного "ответа"


ES5 и более ранние версии

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

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

function extend() {
    for (var o = {}, i = 0; i < arguments.length; i++) {
        // Uncomment to skip arguments that are not objects (to prevent errors)
        // 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 objects
 */
function extend() {
    for (var o = {}, i = 0; i < arguments.length; i++) {
        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;
}

/**
 * Example
 */
document.write(JSON.stringify(extend({
    api: 1,
    params: {
        query: 'hello'
    }
}, {
    params: {
        query: 'there'
    }
})));
// outputs {"api": 1, "params": {"query": "there"}}

Этот ответ теперь - капля в море ...

Самый короткий ответ, который отлично работает! Он заслуживает большего количества голосов!

Jens Törnell 11.02.2020 10:09

Мы можем создать пустой объект и объединить их с помощью for-loop:

var obj1 = {
  id: '1',
  name: 'name'
}

var obj2 = {
  c: 'c',
  d: 'd'
}

var obj3 = {}

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


console.info( obj1, obj2, obj3)

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"}

С помощью следующего помощника вы можете объединить два объекта в один новый объект:

function extend(obj, src) {
    for (var key in src) {
        if (src.hasOwnProperty(key)) obj[key] = src[key];
    }
    return obj;
}

// example
var a = { foo: true }, b = { bar: false };
var c = extend(a, b);

console.info(c);
// { foo: true, bar: false }

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

Если поддержка IE 8 не требуется, вы можете использовать Object.keys для тех же функций:

function extend(obj, src) {
    Object.keys(src).forEach(function(key) { obj[key] = src[key]; });
    return obj;
}

Это требует немного меньше кода и немного быстрее.

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

ES2018 / TypeScript: Многие ответы в порядке, но я придумал более элегантное решение этой проблемы, когда вам нужно объединить два объекта без перезаписи перекрывающихся ключей объекта.

Моя функция также принимает неограниченное количество объектов для слияния в качестве аргументов функции:

(Здесь я использую нотацию TypeScript, не стесняйтесь удалять тип :object[] в аргументе функции, если вы используете простой JavaScript).

const merge = (...objects: object[]) => {
  return objects.reduce((prev, next) => {
    Object.keys(prev).forEach(key => {
      next[key] = { ...next[key], ...prev[key] }
    })
    return next
  })
}

Кажется, это не работает? typescript-play.js.org/#code/…

Chris Stryczynski 04.04.2019 14:54

объединить два объекта с помощью Object.assign и оператор распространения.

Неправильный способ (изменить исходный объект из-за ориентации на o1)

var o1 = { X: 10 };
var o2 = { Y: 20 };
var o3 = { Z: 30 };
var merge = Object.assign(o1, o2, o3);
console.info(merge)  // {X:10, Y:20, Z:30}
console.info(o1)     // {X:10, Y:20, Z:30}

Правильные пути

  • Object.assign ({}, o1, o2, o3) ==> нацеливание на новый объект

  • {... o1, ... o2, ... o3} ==> распространяющиеся объекты

var o1 = { X: 10 };
var o2 = { Y: 20 };
var o3 = { Z: 30 };

console.info('Does not modify original objects because target {}');
var merge = Object.assign({}, o1, o2, o3);
console.info(merge); // { X: 10, Y: 20, Z: 30 }
console.info(o1)

console.info('Does not modify original objects')
var spreadMerge = {...o1, ...o2, ...o3};
console.info(spreadMerge);
console.info(o1);

Кажется, это должно быть все, что вам нужно:

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

var obj1 = { ...obj1, ...obj2 }

После этого obj1 должен иметь следующее значение:

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

мелкий

var obj = { name : "Jacob" , address : ["America"] }
var obj2 = { name : "Shaun" , address : ["Honk Kong"] }

var merged = Object.assign({} , obj,obj2 ); //shallow merge 
obj2.address[0] = "new city"

result.address [0] изменен на «новый город», т.е. объединенный объект также изменится. Это проблема неглубокого слияния.

глубокий

var obj = { name : "Jacob" , address : ["America"] }
var obj2 = { name : "Shaun" , address : ["Honk Kong"] }

var result = Object.assign({} , JSON.parse(JSON.stringify(obj)),JSON.parse(JSON.stringify(obj2)) )

obj2.address[0] = "new city"

result.address [0] не изменяется

var merged = Object.assign ({}, obj, obj2) ничего не объединяет?

GroovyDotCom 16.11.2020 23:32

Вы можете использовать метод Object.assign. Например:

var result = Object.assign(obj1, obj2);

Также обратите внимание, что он создает неглубокую копию объекта.

Use Spread operator which follows the ES6 version

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }
let result = {...obj1,...obj2};
console.info(result)

output { food: 'pizza', car: 'ford', animal: 'dog' }

Есть разные способы добиться этого:

Object.assign(targetObj, sourceObj);

targetObj = {...targetObj, ...sourceObj};

Object.assign(TargetObject, Obj1, Obj2, ...);

Это можно сделать тремя способами:

Подход 1: -

// using spread ...
    let obj1 = {
        ...obj2
    };

Подход 2: -

// using  Object.assign() method
let obj1 = Object.assign({}, obj2);

Подход 3: -

// using JSON
let obj1 = JSON.parse(JSON.stringify(obj2));

Другие вопросы по теме