Мне нужно объединить два (очень простых) объекта JavaScript во время выполнения. Например, я бы хотел:
var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }
obj1.merge(obj2);
//obj1 now has three properties: food, car, and animal
У кого-нибудь есть сценарий для этого или есть встроенный способ сделать это? Мне не нужна рекурсия, и мне не нужно объединять функции, а только методы на плоских объектах.
Кстати, несколько верхних ответов выполняют «неглубокое» слияние: если один и тот же ключ существует как в obj1, так и в obj2, значение в obj2 сохраняется, значение в obj1 отбрасывается. Например. если в примере вопроса был var obj2 = { animal: 'dog', food: 'bone' };, слияние будет { food: 'bone', car: 'ford', animal: 'dog' }. Если вы работаете с «вложенными данными» и хотите «глубокое слияние», поищите ответы, в которых упоминается «глубокое слияние» или «рекурсия». Если у вас есть значения arrays, используйте параметр arrayMerge в github «TehShrike / deepmerge», как упоминалось в здесь.
Пожалуйста, перейдите по ссылке ниже stackoverflow.com/questions/171251/… Оператор спреда, который является новой функцией в версии ES6



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Стандартный метод 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;
}
Это не работает, если объекты имеют атрибуты с одинаковыми именами, и вы также можете объединить атрибуты.
Это только поверхностное копирование / слияние. Имеет потенциал уничтожить множество элементов.
+1 за признание того, что некоторые бедняги вынуждены использовать фреймворки, которые гадят на всех их прототипах ...
Прототип имеет это:
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.
Если подумать, это, вероятно, должен быть Object.prototype, а не Object ... Хорошая идея или нет, это то, что делает фреймворк prototype.js, и поэтому, если вы используете его или другой фреймворк, который зависит от него, Object.prototype уже будет расширен. с extend.
Я не хочу вас кричать, но сказать, что это нормально, потому что prototype.js делает это, - плохой аргумент. Prototype.js популярен, но это не значит, что они являются образцом для подражания в том, как создавать JavaScript. Ознакомьтесь с подробным обзором библиотеки Prototype, написанным профессионалом JS. dhtmlkitchen.com/?category=/JavaScript/&date=2008/06/17/…
Я не имею в виду, что это нормально - я имею в виду, что он может уже быть там, поэтому вам может не понадобиться собственный extend.
Какая разница, правильно это или неправильно? Если это работает, значит, работает. Ожидание идеального решения - это здорово, кроме случаев, когда вы пытаетесь сохранить контракт, привлечь новых клиентов или уложиться в сроки. Дайте здесь всем, кто увлечен программированием, бесплатные деньги, и они в конечном итоге все сделают «правильно».
Дэвид, потому что вы хотите, чтобы ваш сайт продолжал работать при изменении браузеров. Решение Йоги, я не думаю, что что-то не так с тем, как Prototype добавляет вещи в Object.prototype. Возможность добавлять новые методы путем изменения constructor.prototype - это тот же способ, которым JS выполняет объектно-ориентированный подход. Он просто немного отличается от других объектно-ориентированных языков, с которыми вы, возможно, работали. Одним из исключений является Ruby, который также позволяет вам изменять существующие экземпляры и классы с помощью расширения и включения (включить в Ruby ~ constructor.prototype.extend в JS).
В документации Mozilla указано, что Object.prototype предоставляется именно для этой цели: developer.mozilla.org/en/Core_JavaScript_1.5_Reference/…
Где вы все нашли Object.prototype? Object.extend не будет мешать вашим объектам, он просто присоединяет функцию к объекту Object. И obj1.extend(obj2) не является правильным способом активации функции, используйте Object.extend(obj1, obj2) insted
@allyourcode - я не читаю это в документации - хотя, конечно, они могли измениться после вашего комментария. AFAIK, основное использование Object.prototype состоит в том, чтобы иметь стандартный набор методов / свойств, которые известны всем объектам, и отвергать, если необходимо, в конструкторах новых объектов (которые, таким образом, эквивалентны «подклассам» в OO). Важно отметить, что такой отвергать не изменяет сам Object.prototype; он изменяет поведение вашего нового «подкласса». В документации ничего не говорится о том, хорошо или плохо добавление настраиваемых свойств в Object.prototype.
повторно комментарий "добавить в Object.prototype". "обезьяна-патч" - Документы уточняют, что нет следует расширять Object.Prototype. В developer.mozilla.org/en-US/docs/Web/JavaScript/… «Плохая практика: расширение собственных прототипов. Одна из ошибок, которая часто используется, - это расширение Object.prototype или одного из других встроенных прототипов. Этот метод называется исправлением обезьяны и нарушает инкапсуляцию. Хотя он используется популярными фреймворками, такими как Prototype.js, по-прежнему нет веских причин загромождать встроенные типы дополнительными нестандартными функциями ".
В 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);
Имейте в виду, что jQuery.extend также имеет глубокую (логическую) настройку. jQuery.extend(true,settings,override), что важно, если свойство в настройках содержит объект, а переопределение имеет только часть этого объекта. Вместо удаления несогласованных свойств глубокая настройка будет обновляться только там, где она существует. По умолчанию - false.
Я искал код для объединения свойств объекта и оказался здесь. Однако, поскольку не было кода для рекурсивного слияния, я написал его сам. (Может быть, расширение 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 также будет изменен, поскольку объекты передаются по ссылке.
Это было то, что я искал. Обязательно проверьте, есть ли obj2.hasOwnProperty (p), прежде чем переходить к оператору try catch. В противном случае вы можете столкнуться с каким-то другим дерьмом, которое будет сливаться с более высоких уровней цепочки прототипов.
Спасибо, Маркус. Обратите внимание, что o1 также изменяется с помощью MergeRecursive, поэтому в конце o1 и o3 идентичны. Это может быть более интуитивно понятным, если вы ничего не возвращаете, чтобы пользователь знал, что то, что они предоставляют (o1), изменяется и становится результатом. Кроме того, в вашем примере, возможно, стоит добавить в o2 что-то, чего изначально не было в o1, чтобы показать, что свойства o2 вставляются в o1 (кто-то другой ниже представил альтернативный код, копирующий ваш пример, но их код ломается, когда o2 содержит свойства, не в o1 - но ваш на этот счет работает).
Я не уверен, чем это отличается от stackoverflow.com/a/171256/351426. Используя тот же ввод, он дает тот же результат. Что в твоем делает его более «выздоравливающим»?
@JonoRR - вызов самого себя делает его рекурсивным: obj1 [p] = MergeRecursive (obj1 [p], obj2 [p]);
Это хороший ответ, но пара придирок: 1) Никакая логика не должна основываться на исключениях; в этом случае любое выброшенное исключение будет перехвачено с непредсказуемыми результатами. 2) Я согласен с комментарием @ pancake о том, что ничего не возвращать, поскольку исходный объект изменен. Вот пример jsFiddle без логики исключения: jsfiddle.net/jlowery2663/z8at6knn/4 - Джефф Лоури
@JeffLowery Хороший пример скрипки: if (obj2 [p]! == null && obj2 [p] .constructor == Object) {пропускает нулевые значения для
Использование попробуй поймать совершенно бесплатно, и obj2[p].constructor==Object во многих случаях выйдет из строя.
Кроме того, obj2 [p] .constructor == Array необходим в любом случае, когда есть массив объектов.
Чтобы включить глубокие копии объектов пустой, измените if ( obj2[p].constructor==Object ) { на if ( obj2[p].constructor==Object && (Object.keys(obj2[p]).length > 0) ) {. Однако это значительно замедлит его.
@ GreenRaccoon23 Если obj2 [p] пуст, значит, у него нет свойств, поэтому MergeRecursive(obj1[p], obj2[p]) оставит obj1[p] без изменений - как и должно. «глубокая копия пустого объекта» не имеет смысла - нет свойств, которые можно объединить в 1 - приведите пример того, что вы пытаетесь сделать, чего не делает существующий код.
Приведенные решения должны быть изменены для проверки 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);
function merge(obj1, obj2) { return JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)) .replace(/\}\{/g, ',').replace(/,\}/g, '}').replace(/\{,/g, '{')); }
или как однострочник: objMerge = JSON.parse((JSON.stringify(obj1) + JSON.stringify(obj2)).replace(/\}\{/, ", "));
@Charles, извините, но как "этот метод очень опасен", но также забавен - это самый безопасный из возможных. Во-первых, он использует JSON для выполнения тяжелой работы по чтению содержимого объекта, во-вторых, он использует объект JSON, который, безусловно, является самым безопасным машина оптимизирована для повторного анализа JSO из строкового литерала совместимого объекта JSON. А также обратно совместим с IE8, который является первым браузером, реализующим его стандарт. Это просто заставляет меня задаться вопросом: «Почему, ну, почему ты сказал такую глупость и ушел с рук»?
Я считаю, что функции объекта здесь не работают, поскольку нельзя использовать stringify, но можем ли мы прояснить другие недостатки?
Как правило, не использует решения, которые полагаются на разбор, когда есть известные решения, которые не нуждаются в синтаксическом анализе. В этом случае требование, чтобы "} {" не встречалось в строке, кажется "достаточно безобидным", но это может случиться, так что это не лучший метод, на который можно полагаться. И, конечно, он плохо масштабируется (производительность, память) для глубоко вложенных объектов, учитывая, что вы конвертируете в строку и обратно. ИМХО, только не делайте этого.
Лучший способ сделать это - добавить подходящее свойство, которое нельзя перечислить, с помощью 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 не существует
Я расширил метод Дэвида Коллиера:
Если переопределение ложно, никакое свойство не переопределяется, но новые свойства будут добавлены.
Использование: 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 года.
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. Итак, это не слияние, и это не лучше!
нет, не терпит неудач, я только что успешно попробовал то, что вы сказали.
Следует отметить, что функция является деструктивной и obj1а также любые объекты, вложенные в нее, будут изменены, что может быть или не быть тем, что вы хотели. Для ясности я сам переименовал функцию и аргументы в applyProperties(target, source).
@ mindplay.dk - что вы имеете в виду разрушительный? Дело в том, чтобы объединить o2 с o1, а не создавать новый (третий) объект o1, объединенный с o2. Я ответил на точный вопрос
@vsync деструктивен в том смысле, что, например, Array.sort () деструктивен, а Array.slice () - нет - это не критика, просто указание на факт :-)
@ mindplay.dk - в вопросе был задан явно, чтобы объединить один объект с другим, переопределив первый, поэтому очевидно, что это деструктивно по природе рассматриваемого вопроса. для неразрушающего ответа нужно перейти на страницу другого ответа
Мне нужно объединить объекты сегодня, и этот вопрос (и ответы) мне очень помог. Я попробовал некоторые ответы, но ни один из них не соответствовал моим потребностям, поэтому я объединил некоторые ответы, добавил что-то сам и придумал новую функцию слияния. Вот:
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 }));
хороший ответ, но я думаю, что если свойство присутствует в первом и втором, и вы пытаетесь создать новый объект путем слияния первого и второго, тогда уникальные, присутствующие в первом объекте, будут потеряны
Но разве это не ожидаемое поведение? Цель этой функции - переопределить значения в порядке аргументов. Следующий будет иметь приоритет над текущим. Как сохранить уникальные ценности? Из моего примера, чего вы ждете от merge({}, t1, { key1: 1 })?
Я ожидаю объединить только значения, не являющиеся объектно-типизированными.
не выполняется, если ожидаемый результат выглядит следующим образом: gist.github.com/ceremcem/a54f90923c6e6ec42c7daf24cf2768bd
Это сработало для меня в строгом режиме для моего проекта node.js. Пока что это лучший ответ на этот пост.
Это расширенная версия, основанная на Маркус и 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);
Есть еще один, который может иметь значение в зависимости от того, чего вы пытаетесь достичь: _.defaults(object, *defaults) «Заполните пустые и неопределенные свойства в объекте значениями из объектов по умолчанию и верните объект».
Многие люди прокомментировали изменение целевого объекта, но вместо того, чтобы создать новый метод методом, общим шаблоном (например, в dojo) является использование dojo.mixin (dojo.clone (myobj), {newpropertys: "для добавления to myobj "})
Подчеркивание может принимать произвольное количество объектов, поэтому вы можете избежать мутации исходного объекта, выполнив var mergedObj = _.extend({}, obj1, obj2).
Просто если кто использует Библиотека закрытия 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 с подчеркиванием действительно перезаписало, а не слило
Стоит отметить, что typeof array и typeof null вернут 'object' и нарушат это.
@ u01jmg3 см. мой ответ здесь: stackoverflow.com/questions/27936772/… - функция isObject
Это не работает, если вы используете строгий режим для проектов node.js
Это объединяет 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) сделает свое дело
Вот мой удар, который
Короткий :)
/*
Recursively merge properties and return new object
obj1 <- obj2 [ <- ... ]
*/
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], он изменит первый объект, который вы передаете объединенному объекту.
Теперь перестал работать в хроме. Я считаю плохую идею использовать такую конструкцию toString.call(src) == '[object Object]' для проверки наличия объекта или его отсутствия. typeof src намного лучше. Более того, он преобразует массивы в объект (по крайней мере, у меня такое поведение на последней версии Chrome).
Мне нужно было сделать это внутри жадного цикла, я протестировал эту функцию по сравнению с той, которую я использовал до сих пор _.merge (object, [sources]) из lodash (своего рода библиотека подчеркивания): ваша функция почти в два раза быстрее Спасибо друг.
Вы можете назначить каждому объекту метод слияния по умолчанию (возможно, `` унаследовать '' лучшее имя):
Он должен работать либо с объектами, либо с конкретными функциями.
Приведенный ниже код обрабатывает переопределение объединенных значений, если это необходимо:
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.
С помощью 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.
Harmony ECMAScript 2015 (ES6) указывает Object.assign, который это сделает.
Object.assign(obj1, obj2);
Текущая поддержка браузеров - становиться лучше, но если вы разрабатываете браузеры, у которых нет поддержки, вы можете использовать полифил.
Обратите внимание, что это только неглубокое слияние
Я думаю, что пока что у Object.assign довольно приличное покрытие: kangax.github.io/compat-table/es6/…
Теперь это должен быть правильный ответ. В настоящее время люди компилируют свой код для совместимости с редким браузером (IE11), который не поддерживает такого рода вещи. Боковое примечание: если вы не хотите добавлять obj2 в obj1, вы можете вернуть новый объект, используя Object.assign({}, obj1, obj2)
@Duvrai Согласно отчетам, которые я видел, IE11 точно не редкость составлял около 18% доли рынка по состоянию на июль 2016 года.
Это решение создает новый объект и может обрабатывать несколько объектов.
Кроме того, это рекурсивный, и вы можете выбрать погоду от хочу до перезаписать значения и Объекты.
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)
Обратите внимание, что указанные выше методы изменяют исходный объект.
Метод lodash сработал для меня, так как я хотел объединить два объекта и сохранить свойства, вложенные более чем на один или два уровня в глубину (а не просто заменить / стереть их).
правда @Wtower. Просто из-за этого был баг. Лучше всего убедиться, что он всегда начинается как _.merge ({}, ...
Другой способ:
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 ] )
Этот вопрос не о jQuery. JavaScript! = JQuery
Я искал в Google JavaScript, но это более простой подход
Если вы используете Набор инструментов 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.
Метод 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.
Спасибо за пояснение, что Objects.assign возвращает объединенный объект, которого нет в других ответах. Это более функциональный стиль программирования.
Вы должны использовать 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);Поддержка браузера?
@ Alph.Dev Вы знаете caniuse.com?
Я не могу заставить работать трехточечный синтаксис в node.js. узел жалуется на это.
В EcmaScript2016 вы можете сделать следующее
Исправление: это предложение стадии 3, но оно всегда работало для меня.
const objA = {
attrA: 'hello',
attrB: true
}
const objB = {
attrC: 2
}
const mergedObj = {...objA, ...objB}
Я рекомендую использовать неразрушающие методы, которые не изменяют исходный источник, 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 и более ранние версии - есть много других ответов, касающихся 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"}}Этот ответ теперь - капля в море ...
Самый короткий ответ, который отлично работает! Он заслуживает большего количества голосов!
Мы можем создать пустой объект и объединить их с помощью 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/…
объединить два объекта с помощью 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) ничего не объединяет?
Вы можете использовать метод 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));
Стоит отметить этот ответ на аналогичный вопрос, который показывает, как объединить «на один уровень ниже». То есть он объединяет значения повторяющихся ключей (вместо перезаписи первого значения вторым значением), но не выполняет рекурсию дальше этого. IMHO, это хороший чистый код для этой задачи.