Merge two objects without override

前端 未结 6 1483
北荒
北荒 2020-12-17 10:40

I have a defaultObject like that:

var default = {
    abc: \"123\",
    def: \"456\",
    ghi: {
       jkl: \"789\",
       mno: \"012\"
    }
};

相关标签:
6条回答
  • 2020-12-17 10:54

    With ES2015 now being supported in all modern browsers, the native Object.assign can be used to extend objects

    Object.assign({}, _default, values)
    

    Object.assign

    Note that default is a reserved keyword, and can't be used as a variable name


    The original answer, written in 2013 :

    Since this is tagged with jQuery, you could use $.extend for a simple cross-browser solution

    var temp = {};
    $.extend(true, temp, _default, values);
    values = temp;
    

    0 讨论(0)
  • 2020-12-17 11:01

    For those who don't use jQuery, here comes a vanilla-js solution.

    Solution:

    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;
    }
    

    Compressed (with Closure Compiler):

    Only 199 characters!

    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}
    

    How to use:

    extend(target, obj1, obj2); // returns target
    

    If you only want to merge, use

    var merged = extend({}, obj1, obj2);
    

    Features:

    • It doesn't look at objects' prototype.
    • Ignores non-objects.
    • It is recursive in order to merge properties which are objects.
    • Objects referenced in target's properties, if extended, are replaced by new ones, and the original ones are not modified.
    • In case of same property names, the merged value will be the merging of the objects after the last (in the order of arguments) non-object value. Or, if the last isn't an object, itself.

    Examples:

    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}}
    

    Warning:

    Be aware that if browser is not clever enough, it could be trapped in an infinite loop:

    var obj1={},
        obj2={};
    obj1.me=obj1;
    obj2.me=obj2;
    extend({},obj1,obj2);
    

    If the browser is clever enough, it can throw an error, or return {me: undefined}, or whatever.

    Note that this warning also applies if you use jQuery's $.extend.

    0 讨论(0)
  • 2020-12-17 11:03

    My version based on Oriol's answer adds a check for arrays, so that arrays don't get transformed into funny {'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;
    }
    
    0 讨论(0)
  • 2020-12-17 11:04

    Also if you are happy with ES6:

    Object.assign({}, default, values)
    
    0 讨论(0)
  • 2020-12-17 11:05

    Another way would be to simply extend value by default (instead of the other way around which would override default)

    Object.assign(value, default) // values of default overrides value
    default = value // reset default to value
    

    The caveat here is that the content of value is changed as well. Upside is no library is required and easier than the plain vanilla solution above.

    0 讨论(0)
  • 2020-12-17 11:11

    I found that the easiest way to to this is to use mergeWith() from lodash ( https://lodash.com/docs/4.17.15#mergeWith ) which accepts a customizer function that decides what to do with every property merge. Here is one that is recursive :

    const mergeFunction = (objValue, srcValue) => {
      if (typeof srcValue === 'object') {
        _.mergeWith(objValue, srcValue, mergeFunction)
      } else if (objValue) {
        return objValue
      } else {
        return srcValue
      }
    }
    
    0 讨论(0)
提交回复
热议问题