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

How can I merge properties of two JavaScript objects dynamically?

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

1
2
3
4
5
6
var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }

obj1.merge(obj2);

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

有没有人有这个脚本或者知道一个内置的方法来做这个?我不需要递归,也不需要合并函数,只需要平面对象上的方法。


ECMAScript 2018标准方法

您将使用对象排列:

1
2
3
4
5
let merged = {...obj1, ...obj2};

/** There's no limit to the number of objects you can merge.
 *  Later properties overwrite earlier properties with the same name. */

const allRules = {...obj1, ...obj2, ...obj3};

ECMAScript 2015(ES6)标准方法

1
2
3
4
5
6
7
8
/* For the case in question, you would do: */
Object.assign(obj1, obj2);

/** There's no limit to the number of objects you can merge.
 *  All objects get merged into the first object.
 *  Only the object in the first argument is mutated and returned.
 *  Later properties overwrite earlier properties with the same name. */

const allRules = Object.assign({}, obj1, obj2, obj3, etc);

(参见MDN javascript参考)

ES5及更早版本的方法

1
for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }

请注意,这只会将EDOCX1的所有属性(0)添加到obj1中,如果您仍然想使用未修改的obj1的话,这些属性可能不是您想要的。

如果你使用的框架覆盖了你的原型,那么你必须对像hasOwnProperty这样的检查更加熟练,但是这些代码将适用于99%的情况。

示例函数:

1
2
3
4
5
6
7
8
9
10
11
12
/**
 * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
 * @param obj1
 * @param obj2
 * @returns obj3 a new object based on obj1 and obj2
 */

function merge_options(obj1,obj2){
    var obj3 = {};
    for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
    for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
    return obj3;
}


jquery还为此提供了一个实用程序:http://api.jquery.com/jquery.extend/。

摘自jquery文档:

1
2
3
4
5
6
7
// Merge options object into settings object
var settings = { validate: false, limit: 5, name:"foo" };
var options  = { validate: true, name:"bar" };
jQuery.extend(settings, options);

// Now the content of settings object is the following:
// { validate: true, limit: 5, name:"bar" }

上述代码将改变名为settings的现有对象。

如果要在不修改任何参数的情况下创建新对象,请使用以下命令:

1
2
3
4
5
6
7
8
9
var defaults = { validate: false, limit: 5, name:"foo" };
var options = { validate: true, name:"bar" };

/* Merge defaults and options, without modifying defaults */
var settings = $.extend({}, defaults, options);

// The content of settings variable is now the following:
// {validate: true, limit: 5, name:"bar"}
// The 'defaults' and 'options' variables remained the same.


Harmony EcmaScript 2015(ES6)规定了将要执行此操作的Object.assign

1
Object.assign(obj1, obj2);

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


我在谷歌上搜索了合并对象属性的代码,结果就出现在这里。但是,由于没有任何用于递归合并的代码,所以我自己编写了它。(也许jquery扩展是递归的btw?)无论如何,希望其他人也能发现它的用处。

(现在代码不使用Object.prototype)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
* 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;
}

一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

1
2
3
4
5
6
7
8
o3 = {  a : 10,
        b : 2,
        c : {
          ca : 10,
          cb : 20,
          cc : {
            cca : 101,
            ccb : 202 } } };


请注意,underscore.jsextend方法在一行中实现了这一点:

1
2
_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}


与jquery extend()类似,AngularJS中有相同的函数:

1
2
3
4
// Merge the 'options' object into the 'settings' object
var settings = {validate: false, limit: 5, name:"foo"};
var options  = {validate: true, name:"bar"};
angular.extend(settings, options);


今天我需要合并对象,这个问题(和答案)帮助了我很多。我尝试了一些答案,但没有一个符合我的需要,所以我结合了一些答案,自己添加了一些东西,并提出了一个新的合并函数。这里是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
};

一些示例用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 }));


您可以使用对象排列属性,当前是阶段3 ECMAScript建议。

1
2
3
4
5
const obj1 = { food: 'pizza', car: 'ford' };
const obj2 = { animal: 'dog' };

const obj3 = { ...obj1, ...obj2 };
console.log(obj3);


在分配之前,应该修改给定的解决方案以检查for..in循环中的source.hasOwnProperty(property)—否则,您最终会复制整个原型链的属性,这是很少需要的……


在一行代码中合并n个对象的属性

Object.assign方法是ECMAScript 2015(ES6)标准的一部分,它完全满足您的需要。(不支持IE)

1
var clone = Object.assign({}, obj);

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object.

多读…

支持旧浏览器的polyfill:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}


以下两个可能是一个很好的起点。洛达什也有一个定制功能,为那些特殊的需要!

_.extend(http://underlinejs.org/extend)
_.merge(https://lodash.com/docs合并)


顺便说一下,您所做的就是覆盖属性,而不是合并…

这就是javascript对象区域真正合并的方式:只有to对象中不是对象本身的键才会被from覆盖。其他一切都将真正合并。当然,您可以更改此行为,以不覆盖任何存在的内容,例如仅当to[n] is undefined等…

1
2
3
4
5
6
7
8
9
10
11
12
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;
};

用途:

1
var merged = realMerge(obj1, obj2);


这是我的刺

  • 支持深度合并
  • 不改变参数
  • 接受任意数量的参数
  • 不扩展对象原型
  • 不依赖于其他库(jquery、mootools、underline.js等)
  • 包括对属性的检查
  • 简称:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    /*
        Recursively merge properties and return new object
        obj1 &lt;- obj2 [ &lt;- ... ]
    */

    function merge () {
        var dst = {}
            ,src
            ,p
            ,args = [].splice.call(arguments, 0)
        ;

        while (args.length > 0) {
            src = args.splice(0, 1)[0];
            if (toString.call(src) == '[object Object]') {
                for (p in src) {
                    if (src.hasOwnProperty(p)) {
                        if (toString.call(src[p]) == '[object Object]') {
                            dst[p] = merge(dst[p] || {}, src[p]);
                        } else {
                            dst[p] = src[p];
                        }
                    }
                }
            }
        }

       return dst;
    }
  • 例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    a = {
       "p1":"p1a",
       "p2": [
           "a",
           "b",
           "c"
        ],
       "p3": true,
       "p5": null,
       "p6": {
           "p61":"p61a",
           "p62":"p62a",
           "p63": [
               "aa",
               "bb",
               "cc"
            ],
           "p64": {
               "p641":"p641a"
            }
        }
    };

    b = {
       "p1":"p1b",
       "p2": [
           "d",
           "e",
           "f"
        ],
       "p3": false,
       "p4": true,
       "p6": {
           "p61":"p61b",
           "p64": {
               "p642":"p642b"
            }
        }
    };

    c = {
       "p1":"p1c",
       "p3": null,
       "p6": {
           "p62":"p62c",
           "p64": {
               "p643":"p641c"
            }
        }
    };

    d = merge(a, b, c);


    /*
        d = {
           "p1":"p1c",
           "p2": [
               "d",
               "e",
               "f"
            ],
           "p3": null,
           "p5": null,
           "p6": {
               "p61":"p61b",
               "p62":"p62c",
               "p63": [
                   "aa",
                   "bb",
                   "cc"
                ],
               "p64": {
                   "p641":"p641a",
                   "p642":"p642b",
                   "p643":"p641c"
                }
            },
           "p4": true
        };
    */


    赋值()

    ECMAScript 2015(ES6)

    这是一项新技术,是ECMAScript 2015(ES6)标准的一部分。这项技术的规范已经最终确定,但是在各种浏览器中检查兼容性表的使用和实现状态。

    object.assign()方法用于将所有可枚举的自身属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

    1
    2
    3
    4
    5
    6
    7
    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.


    对于不太复杂的对象,可以使用JSON:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var obj1 = { food: 'pizza', car: 'ford' }
    var obj2 = { animal: 'dog', car: 'chevy'}
    var objMerge;

    objMerge = JSON.stringify(obj1) + JSON.stringify(obj2);

    // {"food":"pizza","car":"ford"}{"animal":"dog","car":"chevy"}

    objMerge = objMerge.replace(/\}\{/,","); //  \_ replace with comma for valid JSON

    objMerge = JSON.parse(objMerge); // { food: 'pizza', animal: 'dog', car: 'chevy'}
    // Of same keys in both objects, the last object's value is retained_/

    请注意,在本例中,""不能出现在字符串中!


    最好的方法是使用object.defineproperty添加一个不可枚举的适当属性。

    通过这种方式,您仍然可以迭代对象属性,而不需要使用object.prototype.extend创建新创建的"extend"属性。

    希望这有助于:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Object.defineProperty(Object.prototype,"extend", {
        enumerable: false,
        value: function(from) {
            var props = Object.getOwnPropertyNames(from);
            var dest = this;
            props.forEach(function(name) {
                if (name in dest) {
                    var destination = Object.getOwnPropertyDescriptor(from, name);
                    Object.defineProperty(dest, name, destination);
                }
            });
            return this;
        }
    });

    一旦你开始工作,你就可以:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var obj = {
        name: 'stack',
        finish: 'overflow'
    }
    var replacement = {
        name: 'stock'
    };

    obj.extend(replacement);

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


    您只需使用jquery extend

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

    jQuery.extend(obj1, obj2);

    现在,obj1包含了obj1obj2的所有值。


    Github上有一个叫做deepmerge的图书馆:这似乎得到了一些牵引力。它是一个独立的,通过NPM和Bower软件包经理都可以使用。

    我倾向于在这个问题上使用或改进,而不是从答案中复制粘贴代码。


    原型有:

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

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


    如果有人在使用谷歌关闭库:

    1
    2
    3
    4
    5
    goog.require('goog.object');
    var a = {'a': 1, 'b': 2};
    var b = {'b': 3, 'c': 4};
    goog.object.extend(a, b);
    // Now object a == {'a': 1, 'b': 3, 'c': 4};

    数组存在类似的助手函数:

    1
    2
    3
    4
    var a = [1, 2];
    var b = [3, 4];
    goog.array.extend(a, b); // Extends array 'a'
    goog.array.concat(a, b); // Returns concatenation of array 'a' and 'b'

    合并对象很简单。

    1
    2
    3
    4
    5
    6
    7
    8
    var obj1 = { food: 'pizza', car: 'ford' }
    var obj2 = { animal: 'dog', car: 'BMW' }
    var obj3 = {a:"A"}


    var mergedObj = Object.assign(obj1,obj2,obj3)

    console.log(mergedObj);

    对象从右到左合并,这意味着将覆盖与右对象具有相同属性的对象。

    在本例中,obj2.car覆盖obj1.car


    在moootools中,有object.merge():

    1
    Object.merge(obj1, obj2);

    我扩展了大卫·考利埃的方法:

    • 添加了合并多个对象的可能性
    • 支持深对象
    • override参数(如果最后一个参数是布尔值,则检测到该参数)

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

    用途:对象合并(合并…[,超越];

    这是我的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    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;
        }
    });

    示例和测试用例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    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
    <hr><P>在Ext JS 4中,可以执行以下操作:</P>[cc lang="
    javascript"]var mergedObject = Ext.Object.merge(object1, object2)

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

    请参见合并(对象):对象。


    基于markus和vsync的答案,这是一个扩展版本。函数接受任意数量的参数。它可以用于在DOM节点上设置属性,并对值进行深度复制。但是,第一个参数是通过引用给出的。

    要检测DOM节点,使用is dom node()函数(请参见堆栈溢出问题javascript is dom-如何检查javascript对象是否为DOM对象?)

    它在Opera 11、Firefox 6、Internet Explorer 8和Google Chrome 16中进行了测试。

    代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    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和样式

    1
    2
    3
    4
    mergeRecursive(
      document.getElementById('mydiv'),
      {style: {border: '5px solid green', color: 'red'}},
      {innerHTML: 'Hello world!'});

    合并数组和对象。请注意,Undefined可用于在LeftHand数组/对象中预设值。

    1
    2
    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对象(包括空)的参数都将被忽略。除了第一个参数外,还将丢弃DOM节点。注意,如new string()创建的字符串实际上是对象。

    1
    2
    o = mergeRecursive({a:'a'}, 1, true, null, undefined, [1,2,3], 'bc', new String('de'));
    // o = {0:'d', 1:'e', 2:3, a:'a'}

    如果要将两个对象合并为一个新对象(不影响其中任何一个),请提供作为第一个参数

    1
    2
    3
    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

    编辑(由reapersoon):

    同时合并数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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;
    }

    真的。。这是我看到的第一个多页的StackOverflow帖子。为添加另一个"答案"而道歉

    此方法适用于ES5和早期版本-有许多其他答案可用于ES6。

    我没有看到任何利用arguments属性的"深度"对象合并。下面是我的答案-紧凑递归,允许传递无限的对象参数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function extend() {
        for (var o = {}, i = 0; i < arguments.length; i++) {
            // if (arguments[i].constructor !== Object) continue;
            for (var k in arguments[i]) {
                if (arguments[i].hasOwnProperty(k)) {
                    o[k] = arguments[i][k].constructor === Object ? extend(o[k] || {}, arguments[i][k]) : arguments[i][k];
                }
            }
        }
        return o;
    }

    注释掉的部分是可选的。它将跳过传递的不是对象的参数(防止错误)。

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    extend({
        api: 1,
        params: {
            query: 'hello'
        }
    }, {
        params: {
            query: 'there'
        }
    });

    // outputs {api: 1, params: {query: 'there'}}

    This answer is now but a drop in the ocean ...


    对于underline.js,要合并对象数组,请执行以下操作:

    1
    2
    var arrayOfObjects = [ {a:1}, {b:2, c:3}, {d:4} ];
    _(arrayOfObjects).reduce(function(memo, o) { return _(memo).extend(o); });

    其结果是:

    1
    Object {a: 1, b: 2, c: 3, d: 4}

    1
    2
    3
    4
    5
    var obj1 = { food: 'pizza', car: 'ford' }
    var obj2 = { animal: 'dog' }

    // result
    result: {food:"pizza", car:"ford", animal:"dog"}

    使用jquery.extend()-link

    1
    2
    // Merge obj1 & obj2 to result
    var result1 = $.extend( {}, obj1, obj2 );

    使用.merge()-链接

    1
    2
    // Merge obj1 & obj2 to result
    var result2 = _.merge( {}, obj1, obj2 );

    使用扩展()-链接

    1
    2
    // Merge obj1 & obj2 to result
    var result3 = _.extend( {}, obj1, obj2 );

    使用object.assign()ecmascript 2015(es6)-link

    1
    2
    // Merge obj1 & obj2 to result
    var result4 = Object.assign( {}, obj1, obj2 );

    全输出

    1
    2
    3
    4
    5
    6
    obj1: { animal: 'dog' }
    obj2: { food: 'pizza', car: 'ford' }
    result1: {food:"pizza", car:"ford", animal:"dog"}
    result2: {food:"pizza", car:"ford", animal:"dog"}
    result3: {food:"pizza", car:"ford", animal:"dog"}
    result4: {food:"pizza", car:"ford", animal:"dog"}

    值得一提的是,来自140byt.es集合的版本正在最小空间内解决该任务,因此值得尝试:

    代码:

    1
    function m(a,b,c){for(c in b)b.hasOwnProperty(c)&&((typeof a[c])[0]=='o'?m(a[c],b[c]):a[c]=b[c])}

    用途:

    1
    m(obj1,obj2);

    这是最初的要点。


    你应该使用洛达什的默认深度

    1
    2
    _.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } });
    // → { 'user': { 'name': 'barney', 'age': 36 } }

    我使用纯JavaScript中的以下内容。它从最右边的参数开始,并将它们一直组合到第一个参数。没有返回值,只修改第一个参数,最左边的参数(第一个参数除外)在属性上的权重最高。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var merge = function() {
      var il = arguments.length;

      for (var i = il - 1; i > 0; --i) {
        for (var key in arguments[i]) {
          if (arguments[i].hasOwnProperty(key)) {
            arguments[0][key] = arguments[i][key];
          }
        }
      }
    };

    ES2018/typescript:很多答案都可以,但当您需要合并两个对象而不覆盖重叠的对象键时,我已经想出了一个更优雅的解决方案。

    我的函数还接受无限数量的对象作为函数参数进行合并:

    (我在这里使用的是typescript符号,如果您使用的是纯javascript,请随意删除函数参数中的:object[]类型)。

    1
    2
    3
    4
    5
    6
    7
    8
    const merge = (...objects: object[]) => {
      return objects.reduce((prev, next) => {
        Object.keys(prev).forEach(key => {
          next[key] = { ...next[key], ...prev[key] }
        })
        return next
      })
    }


    我的方式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function mergeObjects(defaults, settings) {
        Object.keys(defaults).forEach(function(key_default) {
            if (typeof settings[key_default] =="undefined") {
                settings[key_default] = defaults[key_default];
            } else if (isObject(defaults[key_default]) && isObject(settings[key_default])) {
                mergeObjects(defaults[key_default], settings[key_default]);
            }
        });

        function isObject(object) {
            return Object.prototype.toString.call(object) === '[object Object]';
        }

        return settings;
    }

    :)


    用途:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //Takes any number of objects and returns one merged object
    var objectMerge = function(){
        var out = {};
        if(!arguments.length)
            return out;
        for(var i=0; i<arguments.length; i++) {
            for(var key in arguments[i]){
                out[key] = arguments[i][key];
            }
        }
        return out;
    }

    试验对象:

    1
    console.log(objectMerge({a:1, b:2}, {a:2, c:4}));

    其结果是:

    1
    { a: 2, b: 2, c: 4 }


    戈西对大卫·考利埃方法的扩展:

    检查这两行:

    1
    2
    from = arguments[i];
    Object.getOwnPropertyNames(from).forEach(function (name) {

    一个需要检查"从"与空对象…例如,如果合并先前在服务器上创建的Ajax响应中的对象,则对象属性的值可以为"null",在这种情况下,上述代码会生成一个错误,说明:

    "from" is not a valid object

    例如,将"…object.getownprotynames(from).foreach…"函数包装为"if(from!"{NULL){…}"将防止该错误发生。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    function extend(o, o1, o2){
        if( !(o instanceof Object) ) o = {};

        copy(o, o1);
        if( o2 )
            copy(o, o2)

        function isObject(obj) {
            var type = Object.prototype.toString.call(obj);
            return obj === Object(obj) && type != '[object Array]' && type != '[object Function]';
        };

        function copy(a,b){
            // copy o2 to o
            for( var key in b )
                if( b.hasOwnProperty(key) ){
                    if( isObject(b[key]) ){
                        if( !isObject(a[key]) )
                            a[key] = Object.assign({}, b[key]);
                        else copy(a[key], b[key])
                    }
                    else
                        a[key] = b[key];
                }
        }

        return o;
    };


    var o1 = {a:{foo:1}, b:1},
        o2 = {a:{bar:2}, b:[1], c:()=>{}},
        newMerged = extend({}, o1, o2);
       
    console.log( newMerged )
    console.log( o1 )
    console.log( o2 )


    在Yui Y.merge中,应完成以下工作:

    1
    Y.merge(obj1, obj2, obj3....)

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


    原型中的正确实现应该如下所示:

    1
    2
    3
    4
    var obj1 = {food: 'pizza', car: 'ford'}
    var obj2 = {animal: 'dog'}

    obj1 = Object.extend(obj1, obj2);

    ES5兼容的本机一行程序:

    1
    var merged = [obj1, obj2].reduce(function(a, o) { for(k in o) a[k] = o[k]; return a; }, {})

    I'm kind of getting started with JavaScript, so correct me if I'm wrong.

    But wouldn't it be better if you could merge any number of objects? Here's how I do it using the native Arguments object.

    The key to is that you can actually pass any number of arguments to a JavaScript function without defining them in the function declaration. You just can't access them without using the Arguments object.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function mergeObjects() (
        var tmpObj = {};

        for(var o in arguments) {
            for(var m in arguments[o]) {
                tmpObj[m] = arguments[o][m];
            }
        }
        return tmpObj;
    }

    这里是我在代码库中用来合并的代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function merge(to, from) {
      if (typeof to === 'object' && typeof from === 'object') {
        for (var pro in from) {
          if (from.hasOwnProperty(pro)) {
            to[pro] = from[pro];
          }
        }
      }
      else{
          throw"Merge function can apply only on object";
      }
    }

    对于那些使用node.js的用户,有一个NPM模块:node.extend

    安装:

    1
    npm install node.extend

    用途:

    1
    2
    3
    var extend = require('node.extend');
    var destObject = extend(true, {}, sourceObject);
    // Where sourceObject is the object whose properties will be copied into another.


    可以通过以下方法合并对象

    1
    2
    3
    4
    5
    6
    7
    var obj1 = { food: 'pizza', car: 'ford' };
    var obj2 = { animal: 'dog' };

    var result = mergeObjects([obj1, obj2]);

    console.log(result);
    document.write("result: [cc lang="javascript"]" + JSON.stringify(result, 0, 3) +"

    "";函数合并对象(ObjectArray){if(objectArray.length){变量b=",i=-1;while(对象数组[++i])。{var str=json.stringify(objectArray[i]);b+=str.切片(1,str.长度-1);if(objectArray[i+1])b+=",";}返回json.parse(""+b+"");}返回{};}<代码> < /PRE>


    合并JSON兼容的javascript对象

    我鼓励使用不修改原始源的非破坏性方法,"object.assign"是一种破坏性方法,而且它也不太适合生产,因为它停止在早期的浏览器上工作,而您没有办法用另一种方法干净地修补它。

    无论解决方案是什么,合并JS对象都将永远是遥不可及或不完整的。但是合并符合JSON的兼容对象离能够编写一个简单的、可移植的代码仅仅一步之遥,这是一种无损的方法,将一系列JS对象合并到一个返回的主对象中,该主对象包含为达到预期目的而在单个主对象中合成的所有唯一属性名及其对应值。

    记住,MSIE8是第一个为JSON对象添加本地支持的浏览器,这是一个很大的缓解,重用现有技术始终是一个受欢迎的机会。

    将代码限制为JSON标准对象比限制更具优势,因为这些对象也可以通过Internet传输。当然,对于那些想要更深层次的向后兼容性的人,总会有一个JSON插件,它的方法可以很容易地分配给外部代码中的JSON变量,而不必修改或重写正在使用的方法。

    1
    2
    3
    4
    5
    function Merge( ){
        var a = [].slice.call( arguments ), i = 0;
            while( a[i] )a[i] = JSON.stringify( a[i++] ).slice( 1,-1 );
            return JSON.parse("{"+ a.join() +"}" );
        }

    (当然,你可以给它起一个更有意义的名字,我还没有决定,应该叫它jsonmerge)

    用例:

    1
    var master = Merge( obj1, obj2, obj3, ...objn );

    现在,与Object.assign相反,这将使所有对象保持原样(如果您做了错误的事情,需要重新排序合并对象,或者在再次合并之前能够将它们单独用于其他操作)。

    合并参数的数量也仅受参数长度限制[这是巨大的]。本机支持的JSON parse/stringify已经进行了机器优化,这意味着:它应该比JS循环的任何脚本形式都快。对给定参数的迭代是使用while完成的,这被证明是JS中最快的循环。

    简单地说,我们已经知道,唯一对象标签(键)的重复属性将被包含相同键标签的后一个对象覆盖,这意味着您可以通过简单地排序或重新排序参数列表来控制哪个属性将取代前一个属性。以及获得一个干净和更新的主对象的好处,没有重复作为最终输出。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ;
    var obj1 = {a:1}, obj2 = {b:2}, obj3 = {c:3}
    ;
    function Merge( ){
        var a = [].slice.call( arguments ), i = 0;
            while( a[i] )a[i] = JSON.stringify( a[i++] ).slice( 1,-1 );
            return JSON.parse("{"+ a.join() +"}" );
        }
    ;
    var master = Merge( obj1, obj2, obj3 )
    ;
    console.log( JSON.stringify( master ) )
    ;


    使用以下助手,可以将两个对象合并为一个新对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function extend(obj, src) {
        for (var key in src) {
            if (src.hasOwnProperty(key)) obj[key] = src[key];
        }
        return obj;
    }

    // example
    var a = { foo: true }, b = { bar: false };
    var c = extend(a, b);

    console.log(c);
    // { foo: true, bar: false }

    这通常在将选项dict与函数或插件中的默认设置合并时很有用。

    如果不需要对IE 8的支持,您可以使用Object.keys来替代相同的功能:

    1
    2
    3
    4
    function extend(obj, src) {
        Object.keys(src).forEach(function(key) { obj[key] = src[key]; });
        return obj;
    }

    这涉及的代码略少,速度稍快。


    另一种方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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);
    }

    我们可以用板条箱装一个空物体,然后用for-loop把它们组合起来:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    var obj1 = {
      id: '1',
      name: 'name'
    }

    var obj2 = {
      c: 'c',
      d: 'd'
    }

    var obj3 = {}

    for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
    for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }


    console.log( obj1, obj2, obj3)


    可以为每个对象指定默认合并(可能是"inherit"更好的名称)方法:

    它应该使用对象或实例化函数。

    如果需要,下面的代码处理覆盖合并值:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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;
        };

    };

    测试数据如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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();

    如果您使用的是Dojo工具包,那么合并两个对象的最佳方法是使用mixin。

    下面是Dojo工具箱mixin的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 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

    更多详情请与我们联系。


    此解决方案创建一个新对象,并能够处理多个对象。

    此外,它是递归的,您可以选择要覆盖值和对象的天气。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
        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的内容:

    1
    {"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/


    1
    2
    3
    4
    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()


    这将obj合并为"违约"def。由于obj被复制到def中,所以obj优先于两者中的任何内容。还要注意,这是递归的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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}}

    我已经使用了object.create()来保留默认设置(使用uuu proto或object.getprototypeof())。

    1
    2
    3
    4
    5
    6
    7
    8
    function myPlugin( settings ){
        var defaults = {
           "keyName": ["string 1","string 2" ]
        }
        var options = Object.create( defaults );
        for (var key in settings) { options[key] = settings[key]; }
    }
    myPlugin( {"keyName": ["string 3","string 4" ] } );

    这样我以后就可以一直使用"concat()"或"push()"。

    1
    var newArray = options['keyName'].concat( options.__proto__['keyName'] );

    注意:为了避免重复,您需要在连接之前进行一次hasownproperty检查。


    实现这一点的一种可能方法是:

    1
    2
    3
    4
    5
    6
    7
    8
    if (!Object.prototype.merge){
        Object.prototype.merge = function(obj){
            var self = this;
            Object.keys(obj).forEach(function(key){
                self[key] = obj[key]
            });
        }
    };

    我不知道是否比其他答案更好。在这个方法中,您将merge function添加到Objects原型中。这样你就可以打电话给obj1.merge(obj2);

    注意:你应该验证你的论点,看看它是否是一个对象,然后"抛出"一个合适的Error。如果不是,Object.keys将"抛出"一个"错误"


    您可以在ecmascript2016中执行以下操作

    更正:这是第三阶段的提议,但它一直对我有效

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const objA = {
      attrA: 'hello',
      attrB: true
    }

    const objB = {
      attrC: 2
    }

    const mergedObj = {...objA, ...objB}


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function extend()
    {
        var o = {};

        for (var i in arguments)
        {
            var s = arguments[i];

            for (var i in s)
            {
                o[i] = s[i];
            }
        }

        return o;
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    [cc lang="javascript"]
    /**
    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"
    }
    */

    < /代码>


    如果您需要一个深度合并,通过在结果中连接数组来"合并"数组,那么这个ES6函数可能就是您需要的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    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);
    1
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    注意,如果只有一个参数传递给这个函数,它就充当一个深度克隆函数。


    使用Object.assign

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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;
    };