合并两个对象而不覆盖
Posted
技术标签:
【中文标题】合并两个对象而不覆盖【英文标题】:Merge two objects without override 【发布时间】:2014-01-02 14:51:51 【问题描述】:我有一个这样的默认对象:
var default =
abc: "123",
def: "456",
ghi:
jkl: "789",
mno: "012"
;
我还有一个喜欢的:
var values =
abc: "zzz",
ghi:
jkl: "yyy",
;
如何将这 2 个对象与以下结果合并(不覆盖)?
var values =
abc: "zzz",
def: "456",
ghi:
jkl: "yyy",
mno: "012"
;
(我不想更改默认对象!)
【问题讨论】:
【参考方案1】:对于那些不使用 jQuery 的人来说,这里有一个 vanilla-js 解决方案。
解决方案:
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;
压缩(使用Closure Compiler):
只有 199 个字符!
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
如何使用:
extend(target, obj1, obj2); // returns target
如果你只想合并,使用
var merged = extend(, obj1, obj2);
特点:
它不查看对象的原型。 忽略非对象。 递归是为了合并对象属性。target
的属性中引用的对象,如果扩展,将被新对象替换,而原始对象不会被修改。
如果属性名称相同,合并值将是最后一个(按参数顺序)非对象值之后的对象的合并。或者,如果最后一个不是对象,则它本身。
示例:
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
警告:
请注意,如果浏览器不够聪明,它可能会陷入无限循环:
var obj1=,
obj2=;
obj1.me=obj1;
obj2.me=obj2;
extend(,obj1,obj2);
如果浏览器足够聪明,它可以抛出错误,或者返回me: undefined
,或者其他什么。
请注意,如果您使用 jQuery 的 $.extend
,此警告也适用。
【讨论】:
【参考方案2】:现在所有现代浏览器都支持 ES2015,原生 Object.assign
可用于扩展对象
Object.assign(, _default, values)
Object.assign
注意default
是保留关键字,不能用作变量名
原始答案,写于 2013 年:
由于这是用 jQuery 标记的,您可以使用 $.extend 来获得简单的跨浏览器解决方案
var temp = ;
$.extend(true, temp, _default, values);
values = temp;
【讨论】:
但这会改变我的默认'default_array',我不想要它,因为它是一个全局变量...... @amp - 这很容易通过使用临时对象来避免。 这个不行,ghi
会被覆盖,不会合并在一起。
是的,正在嵌套的 json 中删除现有对象【参考方案3】:
如果你对 ES6 满意的话:
Object.assign(, default, values)
【讨论】:
如果默认值存在,则仍然覆盖【参考方案4】:另一种方法是在默认情况下简单地扩展值(而不是覆盖默认值的其他方式)
Object.assign(value, default) // values of default overrides value
default = value // reset default to value
这里需要注意的是 value 的内容也发生了变化。好处是不需要库,并且比上面的普通解决方案更容易。
【讨论】:
【参考方案5】:我基于 Oriol 的回答的版本添加了对数组的检查,这样数组就不会变成有趣的 '0': ..., '1': ... thingys
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;
【讨论】:
【参考方案6】:我发现最简单的方法是使用来自 lodash (https://lodash.com/docs/4.17.15#mergeWith) 的 mergeWith()
,它接受一个自定义函数,该函数决定如何处理每个属性合并。这是递归的:
const mergeFunction = (objValue, srcValue) =>
if (typeof srcValue === 'object')
_.mergeWith(objValue, srcValue, mergeFunction)
else if (objValue)
return objValue
else
return srcValue
【讨论】:
以上是关于合并两个对象而不覆盖的主要内容,如果未能解决你的问题,请参考以下文章