我需要能够在运行时合并两个(非常简单)JavaScript对象。例如,我想:
var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }
obj1.merge(obj2);
//obj1 now has three properties: food, car, and animal
是否有一种内置的方法来实现这一点?我不需要递归,也不需要合并函数,只需要平面对象上的方法。
我需要能够在运行时合并两个(非常简单)JavaScript对象。例如,我想:
var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }
obj1.merge(obj2);
//obj1 now has three properties: food, car, and animal
是否有一种内置的方法来实现这一点?我不需要递归,也不需要合并函数,只需要平面对象上的方法。
当前回答
顺便说一句,你们正在做的是覆盖财产,而不是合并。。。
这就是JavaScript对象区域真正合并的方式:只有to对象中不是对象本身的键才会被from覆盖。其他一切都将被真正合并。当然,您可以将此行为更改为不覆盖任何存在的内容,例如仅当to[n]未定义时,等等…:
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);
其他回答
另一种方法:
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);
}
基于Markus和vsync的回答,这是一个扩展版本。该函数接受任意数量的参数。它可以用于设置DOM节点上的财产,并对值进行深度复制。然而,第一个论点是通过引用给出的。
要检测DOM节点,使用isDOMNode()函数(请参阅堆栈溢出问题JavaScript isDOM-如何检查JavaScript对象是否为DOM对象?)
它在Opera 11、Firefox 6、Internet Explorer 8和Google Chrome 16中进行了测试。
Code
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;
}
一些示例
设置HTML元素的innerHTML和样式
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
编辑(由收割者很快):
还要合并阵列
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;
}
您可以为每个对象分配一个默认的合并(也许“继承”一个更好的名称)方法:
它应该使用对象或实例化函数。
如果需要,下面的代码处理重写合并值:
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();
这会将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.log(mergeObjs(a, b));
// {x: {y : [123], z : 123}}
gossi对David Coallier方法的扩展:
检查这两行:
from = arguments[i];
Object.getOwnPropertyNames(from).forEach(function (name) {
需要针对空对象检查“from”。。。例如,如果合并以前在服务器上创建的来自Ajax响应的对象,则对象属性的值可以为“null”,在这种情况下,上述代码会生成一个错误消息:
“from”不是有效对象
因此,例如,将“…Object.getOwnPropertyNames(from).forEach…”函数包装为“if(from!=null){…}”将防止发生该错误。