Tengo un objeto predeterminado como ese:
var default = {
abc: "123",
def: "456",
ghi: {
jkl: "789",
mno: "012"
}
};
Y tengo otro como:
var values = {
abc: "zzz",
ghi: {
jkl: "yyy",
}
};
¿Cómo puedo fusionar esos 2 objetos con el siguiente resultado (sin anular)?
var values = {
abc: "zzz",
def: "456",
ghi: {
jkl: "yyy",
mno: "012"
}
};
(¡No quiero cambiar el objeto predeterminado!)
Oriol
Para aquellos que no usan jQuery, aquí viene una solución vanilla-js.
Solución:
function extend (target) {
for(var i=1; i<arguments.length; ++i) {
var from = arguments[i];
if(typeof from !== 'object') continue;
for(var j in from) {
if(from.hasOwnProperty(j)) {
target[j] = typeof from[j]==='object'
? extend({}, target[j], from[j])
: from[j];
}
}
}
return target;
}
Comprimido (con Compilador de cierre):
¡Solo 199 caracteres!
var extend=function e(c){for(var d=1;d<arguments.length;++d){var a=arguments[d];if("object"===typeof a)for(var b in a)a.hasOwnProperty(b)&&(c[b]="object"===typeof a[b]?e({},c[b],a[b]):a[b])}return c}
Cómo utilizar:
extend(target, obj1, obj2); // returns target
Si solo desea fusionar, use
var merged = extend({}, obj1, obj2);
Características:
- No mira el prototipo de los objetos.
- Ignora los no objetos.
- Es recursivo para fusionar propiedades que son objetos.
- Objetos referenciados en
target
Las propiedades de, si se amplían, se sustituyen por otras nuevas y las originales no se modifican. - En el caso de los mismos nombres de propiedad, el valor fusionado será la fusión de los objetos después del último (en el orden de los argumentos) valor que no es objeto. O, si el último no es un objeto, en sí mismo.
Ejemplos:
extend({}, {a:1}, {a:2}); // {a:2}
extend({}, {a:1}, {b:2}); // {a:1, b:2}
extend({}, {a: {b:1}}, {a: {b:2}}); // {a: {b:2}}
extend({}, {a: {b:1}}, {a: {c:2}}); // {a: {b:2, c:2}}
extend({}, {a: {a:1}}, {a: {b:2}}, {a: 'whatever non object'});
// {a: "whatever non object"}
extend({}, {a: {a:1}}, {a: {b:2}}, {a: 'whatever non object'}, {a: {c:3}},{a: {d:4}});
// {a: {c:3, d:4}}
Advertencia:
Tenga en cuenta que si el navegador no es lo suficientemente inteligente, podría quedar atrapado en un bucle infinito:
var obj1={},
obj2={};
obj1.me=obj1;
obj2.me=obj2;
extend({},obj1,obj2);
Si el navegador es lo suficientemente inteligente, puede arrojar un error o devolver {me: undefined}
o lo que sea.
Tenga en cuenta que esta advertencia también se aplica si usa jQuery’s $.extend
.
-
Un problema. Convierte matrices en objetos.
– usuario3123032
30 de agosto a las 14:20
adeneo
Ahora que ES2015 es compatible con todos los navegadores modernos, el nativo Object.assign
se puede usar para extender objetos
Object.assign({}, _default, values)
Tenga en cuenta que default
es una palabra clave reservada y no se puede utilizar como nombre de variable
La respuesta original, escrita en 2013:
Dado que esto está etiquetado con jQuery, podría usar $.extender para una solución simple entre navegadores
var temp = {};
$.extend(true, temp, _default, values);
values = temp;
-
Pero esto cambiará mi ‘default_array’ predeterminado y no lo quiero, porque es una variable global…
– amplificador
15 de diciembre de 2013 a las 1:21
-
@amp: eso se evita fácilmente usando un objeto temporal.
– adeneo
15 de diciembre de 2013 a las 1:29
-
esto no funciona,
ghi
se sobrescribirán, no se fusionarán.– Nate
12 de junio de 2019 a las 4:04
-
sí, los objetos existentes se eliminan dentro de json anidado
– Phaneendra Charyulu Kanduri
2 de junio de 2021 a las 16:42
Además, si está satisfecho con ES6:
Object.assign({}, default, values)
-
Todavía anula si el valor existe por defecto
–Michael Heuberger
27 de mayo de 2021 a las 7:45
Otra forma sería simplemente ampliar el valor de forma predeterminada (en lugar de lo contrario, lo que anularía el valor predeterminado)
Object.assign(value, default) // values of default overrides value
default = value // reset default to value
La advertencia aquí es que el contenido del valor también cambia. La ventaja es que no se requiere una biblioteca y es más fácil que la solución simple anterior.
Mi versión basada en la respuesta de Oriol agrega una verificación de matrices, para que las matrices no se transformen en cosas divertidas {‘0’: …, ‘1’: …}
function extend (target) {
for(var i=1; i<arguments.length; ++i) {
var from = arguments[i];
if(typeof from !== 'object') continue;
for(var j in from) {
if(from.hasOwnProperty(j)) {
target[j] = typeof from[j]==='object' && !Array.isArray(from[j])
? extend({}, target[j], from[j])
: from[j];
}
}
}
return target;
}
henric malmberg
Descubrí que la forma más fácil de hacerlo es usar mergeWith()
de lodash ( https://lodash.com/docs/4.17.15#mergeWith ) que acepta una función de personalización que decide qué hacer con cada combinación de propiedades. Aquí hay uno que es recursivo:
const mergeFunction = (objValue, srcValue) => {
if (typeof srcValue === 'object') {
_.mergeWith(objValue, srcValue, mergeFunction)
} else if (objValue) {
return objValue
} else {
return srcValue
}
}