如何动态合并两个JavaScript对象的属性?

我需要能够在运行时合并两个(非常简单的)JavaScript对象。例如,我想:

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }

obj1.merge(obj2);

//obj1 now has three properties: food, car, and animal

有没有人为此提供脚本或知道内置的方式来执行此操作?我不需要递归,也不需要合并函数,只需合并平面对象上的方法即可。

阳光猪猪2020/03/09 17:44:03

使用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}
十三村村蛋蛋2020/03/09 17:44:03

let obj1 = {a:1, b:2};
let obj2 = {c:3, d:4};
let merged = {...obj1, ...obj2};
console.log(merged);

猿飞云斯丁2020/03/09 17:44:03

MooTools中,有Object.merge()

Object.merge(obj1, obj2);
神乐前端2020/03/09 17:44:03

Ext JS 4中,可以按照以下步骤进行操作:

var mergedObject = Ext.Object.merge(object1, object2)

// Or shorter:
var mergedObject2 = Ext.merge(object1, object2)

请参见merge(object):Object

斯丁猿2020/03/09 17:44:03

我扩展了David Coallier的方法:

  • 增加了合并多个对象的可能性
  • 支持深层物体
  • 覆盖参数(如果最后一个参数是布尔值则检测到)

如果override为false,则不会覆盖任何属性,但会添加新属性。

用法: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中的对象比较

神奇阿良Jim2020/03/09 17:44:03

做到这一点的最佳方法是添加一个使用Object.defineProperty不可枚举的适当属性。

这样,您仍然可以遍历对象的属性,而不必具有使用Object.prototype.extend创建属性时将获得的新创建的“ extend”。

希望这会有所帮助:

Object.defineProperty(Object.prototype,“ extend”,{
    枚举:错误,
    值:功能(来自){
        var props = Object.getOwnPropertyNames(from);
        var dest = this;
        props.forEach(function(name){
            如果(名称中的名称){
                var destination = Object.getOwnPropertyDescriptor(from,name);
                Object.defineProperty(目的地,名称,目的地);
            }
        });
        返回这个
    }
});

工作完成后,您可以执行以下操作:

var obj = {
    名称:“ stack”,
    完成:“溢出”
}
var替换= {
    名称:“ stock”
};

obj.extend(replacement);

我刚刚在这里写了一篇博客文章:http : //onemoredigit.com/post/1527191998/extending-objects-in-node-js

神无阳光2020/03/09 17:44:03

原型具有:

Object.extend = function(destination,source) {
    for (var property in source)
        destination[property] = source[property];
    return destination;
}

obj1.extend(obj2) 会做你想要的。

StafanDavaid2020/03/09 17:44:03

您可以简单地使用jQuery extend

var obj1 = { val1: false, limit: 5, name: "foo" };
var obj2 = { val2: true, name: "bar" };

jQuery.extend(obj1, obj2);

现在obj1包含的所有值obj1obj2

2020/03/09 17:44:03

有一个叫库deepmergeGitHub上:这似乎是得到一些牵引。它是独立的,可通过npm和bower软件包管理器使用。

我倾向于对此进行使用或改进,而不是从答案中粘贴粘贴代码。

EvaPro2020/03/09 17:44:03

给出的解决方案应进行修改,以检查source.hasOwnProperty(property)for..in分配前环-否则,你最终会复制整个原型链,这是很少所需的属性...

2020/03/09 17:44:03

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.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1);  // { a: 1, b: 2, c: 3 }, target object itself is changed.
Itachi凯2020/03/09 17:44:03

以下两个可能是一个很好的起点。lodash还具有满足这些特殊需求的定制器功能!

_.extendhttp://underscorejs.org/#extend
_.mergehttps://lodash.com/docs#merge

A小卤蛋Pro2020/03/09 17:44:03

我今天需要合并对象,这个问题(和答案)对我有很大帮助。我尝试了一些答案,但都不满足我的需求,因此我组合了一些答案,自己添加了一些内容,并提出了新的合并功能。这里是:

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.log(merge(t1, t2));
console.log(merge(t1, t3));
console.log(merge(t2, t3));
console.log(merge(t1, t2, t3));
console.log(merge({}, t1, { key1: 1 }));
Davaid番长2020/03/09 17:44:03

我用谷歌搜索代码以合并对象属性,并在这里结束。但是,由于没有用于递归合并的任何代码,因此我自己编写了它。(也许jQuery扩展是递归BTW吗?)无论如何,希望其他人也会发现它也有用。

(现在代码不使用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 } } };
Itachi小卤蛋凯2020/03/09 17:44:03

和谐的ECMAScript 2015年(ES6)指定Object.assign将做到这一点。

Object.assign(obj1, obj2);

当前的浏览器支持越来越好,但是如果您正在开发不支持的浏览器,则可以使用polyfill