Как динамически объединить свойства двух объектов JavaScript?
Мне нужно иметь возможность объединить два (очень простых) объекта JavaScript во время выполнения. Например, я хотел бы:
var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }
obj1.merge(obj2);
//obj1 now has three properties: food, car, and animal
У кого-нибудь есть сценарий для этого или знаете встроенный способ сделать это? Мне не нужна рекурсия, и мне не нужно объединять функции, только методы на плоских объектах.
72 ответа
Вы можете назначить каждому объекту метод слияния по умолчанию (возможно, "унаследовать" лучшее имя):
Он должен работать как с объектами, так и с экземплярами функций.
Приведенный ниже код обрабатывает переопределение объединенных значений, если это необходимо:
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.log('screaming like a mammal')};
}
var Human = function () {
this.thinking_brain = true;
this.say = function() {console.log('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 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/
Другой метод:
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);
}
Если вы используете Dojo Toolkit, тогда лучший способ объединить два объекта - использовать миксин.
Ниже приведен пример миксина 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.log(a); // b:c, d:f, g:h
});
// Dojo < 1.7
var a = { b:"c", d:"e" };
dojo.mixin(a, { d:"f", g:"h" });
console.log(a); // b:c, d:f, g:h
Для более подробной информации, пожалуйста, mixin.
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()}
function extend()
{
var o = {};
for (var i in arguments)
{
var s = arguments[i];
for (var i in s)
{
o[i] = s[i];
}
}
return o;
}
<pre>
/**
This script can merge two multi dimensional associative array/objects in javascript by comparing given object with its reference and
will remove additional given keys, adding missed parameteres and also validating values without overhead. Also it will return the default values if no input presented with re-usable reference!
Tested on IE8 and greater.
**/
var module = (function(){
//To make our reference variable onchangable, have to put it into a function which is fster and more efficient than "JSON.parse(JSON.stringify(VARIABLE))"
var _defs = function(){
return {
//string, number and boolean are actually regex based validation keys on input values.
a: ["string", 'Defaul value for "a"'],
b: ["number", 300],
c: ["boolean", true],
d: {
da: ["boolean", true],
db: ["string", 'Defaul value for "db"'],
dc: {
dca: ["number", 200],
dcb: ["string", 'Default value for "dcb"'],
dcc: ["number", 500],
dcd: ["boolean", true]
},
dce: ["string", 'Default value for "dce"'],
},
e: ["number", 200],
f: ["boolean", 0],
g: ["", 'This is an internal extra parameter']
}
}
var _validation = {
number: function (defaultValue, userValue) {
if(/^[0-9]+$/.test(userValue)) //Only numbers allowed
return userValue;
else return defaultValue;
},
string: function (defaultValue, userValue) {
if(/^[a-zA-Z\s]*$/.test(userValue)) //Only A to Z case insentitive with space aloowed.
return userValue;
else return defaultValue;
},
boolean: function (defaultValue, userValue) {
if(typeof userValue === 'boolean') //True or False or 0 ,1
return userValue;
else return defaultValue;
}
}
var _uniqueMerge = function(opts, _ref){
for(var key in _ref)
if (_ref && _ref[key] && _ref[key].constructor && _ref[key].constructor === Object)
_ref[key] = _uniqueMerge((opts ? opts[key] : null ), _ref[key] );
else if(opts && opts.hasOwnProperty(key))
_ref[key] = _validation[_ref[key][0]](_ref[key][1], opts[key]); //or without validation on user enties => ref[key] = obj[key]
else _ref[key] = _ref[key][1];
return _ref;
}
var _get = function(inputs){
return _uniqueMerge(inputs, _defs());
}
return {
options: function(){
return _get(arguments[0] || null); // for more safety and control on number of input variables! used --> ( arguments[0] || null )
}
}
})();
//How to use it:
input_one = {
a : "Hello World",
//b : ["number", 400], //User missed this parameter
c: "Hi",
d : {
da : false,
db : "Hellow! World", // ! is not allowed
dc : {
dca : 10,
dcb : "My String",
dcc: "3thString",
dcd : false
},
dce: "ANOTHER STRING",
},
e: 40,
f: true,
z: 'x'
};
console.log( JSON.stringify( module.options(input_one), null ,2 ) );
//Output:
/*
{
"a": "Hello World",
"b": 300,
"c": true,
"d": {
"da": false,
"db": "Defaul value for \"db\"",
"dc": {
"dca": 10,
"dcb": "My String",
"dcc": 500,
"dcd": false
},
"dce": "ANOTHER STRING"
},
"e": 40,
"f": true,
"g": "This is an internal extra parameter"
}
*/
input_two = {
a : 32,
//b : ["number", 400], //User missed this parameter
c: "Hi",
d : {
da : false,
db : "HelloWorld",
dc : {
dca : 10,
dcb : "My String",
dcd : false
},
dce: 73,
}
};
console.log( JSON.stringify( module.options(input_two), null ,2 ) );
//output
/*
{
"a": "Defaul value for \"a\"",
"b": 300,
"c": true,
"d": {
"da": false,
"db": "HelloWorld",
"dc": {
"dca": 10,
"dcb": "My String",
"dcc": 500,
"dcd": false
},
"dce": "Default value for \"dce\""
},
"e": 200,
"f": 0,
"g": "This is an internal extra parameter"
}
*/
//Empty input will return the default values!
console.log( JSON.stringify( module.options(), null ,2 ) );
//Output
/*
{
"a": "Defaul value for \"a\"",
"b": 300,
"c": true,
"d": {
"da": true,
"db": "Defaul value for \"db\"",
"dc": {
"dca": 200,
"dcb": "Default value for \"dcb\"",
"dcc": 500,
"dcd": true
},
"dce": "Default value for \"dce\""
},
"e": 200,
"f": 0,
"g": "This is an internal extra parameter"
}
*/
</pre>
Вы можете сделать следующее в EcmaScript2016
Исправление: это предложение 3-го этапа, но оно всегда работало для меня
const objA = {
attrA: 'hello',
attrB: true
}
const objB = {
attrC: 2
}
const mergedObj = {...objA, ...objB}
Если вам нужно глубокое слияние, которое также "объединит" массивы путем объединения их в результате, тогда вам может пригодиться эта функция 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.log(merged);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Обратите внимание, что если в эту функцию передается только один аргумент, она действует как функция глубокого клонирования.
Попробуйте так, используя библиотеку jQuery
let obj1 = { food: 'pizza', car: 'ford' }
let obj2 = { animal: 'dog' }
console.log(jQuery.extend(obj1, obj2))
Использование object.assign
Object.prototype.assign = Object.assign && function () {
var a = [];
for (var _i = 0; _i < arguments.length; _i++) {
a[_i] = arguments[_i];
}
var src = a.slice(1);
var target = a[0];
for (var o in src) {
if (src.hasOwnProperty(o)) {
var keys = Object.keys(src[o]);
var _src = src[o];
for (var k in keys) {
if (keys.hasOwnProperty(k)) {
var _key = keys[k];
target[_key] = _src[_key];
}
}
}
}
return target;
};