【问题标题】:deep merge objects with AngularJS使用 AngularJS 深度合并对象
【发布时间】:2016-01-26 21:02:19
【问题描述】:

通常我会使用浅拷贝对象angular.extend()

这是一个例子:

var object1 = {
  "key": "abc123def456",
  "message": {
    "subject": "Has a Question",
    "from": "example1@example.com",
    "to": "example2@example.com"
   }
};

var object2 = {
  "key": "00700916391"
};

console.log(angular.extend({}, object1, object2));

会给我们:

{
 "key": "00700916391",
 "message": {
   "subject": "Has a Question",
   "from": "example1@example.com",
   "to": "example2@example.com"
  }
}

但是,如果我想合并对象以使父键不会被子对象覆盖,该怎么办:

var object1 = {
  "key": "abc123def456",
  "message": {
    "subject": "Has a Question",
    "from": "example1@example.com",
    "to": "example2@example.com"
   }
};

var object2 = {
  "key": "00700916391",              //Overwrite me
  "message": {                       //Dont overwrite me!
    "subject": "Hey what's up?",     //Overwrite me
    "something": "something new"     //Add me
   }
};

console.log(merge(object1, object2));

会给我们:

{
 "key": "00700916391",
 "message": {
   "subject": "Hey what's up?",
   "from": "example1@example.com",
   "to": "example2@example.com",
   "something": "something new"
  }
}
  • 是否有一个 Angular 函数已经进行了我不知道的深度合并?

  • 如果没有,是否有一种本地方法可以在 javascript 中递归地执行 n 级深度?

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    Angular 1.4 或更高版本

    使用angular.merge:

    extend() 不同,merge() 递归地下降到源对象的对象属性中,执行深层复制。

    angular.merge(object1, object2); // merge object 2 into object 1
    

    Angular 的旧版本:

    简单的递归算法没有理由不工作:)

    假设它们都是 JSON.stringify 或类似的结果:

    function merge(obj1,obj2){ // Our merge function
        var result = {}; // return result
        for(var i in obj1){      // for every property in obj1 
            if((i in obj2) && (typeof obj1[i] === "object") && (i !== null)){
                result[i] = merge(obj1[i],obj2[i]); // if it's an object, merge   
            }else{
               result[i] = obj1[i]; // add it to result
            }
        }
        for(i in obj2){ // add the remaining properties from object 2
            if(i in result){ //conflict
                continue;
            }
            result[i] = obj2[i];
        }
        return result;
    }
    

    Here is a working fiddle

    (注意,这里不处理数组)

    【讨论】:

    • 现在,如果您可以将 n 对象作为参数传递,那就太好了。与angular.extend() 的工作方式类似……迎接挑战? :)
    • @DanKanze 不要在第二个 for... in 中循环遍历 obj2,而是通过 arguments 将其包装在另一个循环中(不要从 0 开始索引,您想跳过第一个对象:) (它不是答案的一部分的原因与没有深度限制或数组处理的原因相同 - 它使一个简单的答案与细节复杂化)如果您在实施此问题时遇到问题 - 欢迎您与我在 JS 房间里。
    • 您省略了 var 声明 i - var result = {}, i;
    • @hooblei 感谢您的注意,在像 SO Markdown 编辑器这样的无 jshint 环境中编写代码时,我总是犯这样的错误。我已经修好了 - 不错。
    • 我认为这个合并函数并不能真正替代 1.4 中的 angular.merge 函数。合并的目的是深度扩展一个对象。您的解决方案创建了一个新对象(结果)并且 obj1 没有以任何方式更改。
    【解决方案2】:

    在新版本的 Angularjs 中,他们添加了合并功能,该功能将执行深层复制。

    对于旧版本,我通过从新版本的 Angularjs 复制合并函数的代码来创建我的自定义函数。以下是相同的代码,

    function merge(dst){
      var slice = [].slice;
      var isArray = Array.isArray;
      function baseExtend(dst, objs, deep) {
        for (var i = 0, ii = objs.length; i < ii; ++i) {
          var obj = objs[i];
          if (!angular.isObject(obj) && !angular.isFunction(obj)) continue;
          var keys = Object.keys(obj);
          for (var j = 0, jj = keys.length; j < jj; j++) {
            var key = keys[j];
            var src = obj[key];
            if (deep && angular.isObject(src)) {
              if (!angular.isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
              baseExtend(dst[key], [src], true);
            } else {
              dst[key] = src;
            }
          }
        }
    
        return dst;
      }
      return baseExtend(dst, slice.call(arguments, 1), true);
    }
    

    希望这会帮助那些想知道为什么 angular.merge 在旧版本中不起作用的人。

    【讨论】:

    • 我赞成你,因为这是最好的答案,因为它使用了角码的修改版本。也许你可以添加一行像“if(angular.hasOwnProperty('merge'))return angular.merge.apply(angular, arguments);”。这意味着如果可用,将使用原生 Angular 版本,使用 时使用 polyfill 版本
    • 几乎......我最终编码的是: if (!angular.hasOwnProperty('merge')) { angular.merge = function(dst) { ... } }
    • 注意 baseExtend() 中的这个 1.4 错误 ...geekswithblogs.net/shaunxu/archive/2015/05/29/… 将 Dates() 视为对象并错误地尝试合并它们
    【解决方案3】:

    angular.merge polyfill for angular

    if (!angular.merge) {
      angular.merge = (function mergePollyfill() {
        function setHashKey(obj, h) {
          if (h) {
            obj.$$hashKey = h;
          } else {
            delete obj.$$hashKey;
          }
        }
    
        function baseExtend(dst, objs, deep) {
          var h = dst.$$hashKey;
    
          for (var i = 0, ii = objs.length; i < ii; ++i) {
            var obj = objs[i];
            if (!angular.isObject(obj) && !angular.isFunction(obj)) continue;
            var keys = Object.keys(obj);
            for (var j = 0, jj = keys.length; j < jj; j++) {
              var key = keys[j];
              var src = obj[key];
    
              if (deep && angular.isObject(src)) {
                if (angular.isDate(src)) {
                  dst[key] = new Date(src.valueOf());
                } else {
                  if (!angular.isObject(dst[key])) dst[key] = angular.isArray(src) ? [] : {};
                  baseExtend(dst[key], [src], true);
                }
              } else {
                dst[key] = src;
              }
            }
          }
    
          setHashKey(dst, h);
          return dst;
        }
    
        return function merge(dst) {
          return baseExtend(dst, [].slice.call(arguments, 1), true);
        }
      })();
    }
    

    【讨论】:

      【解决方案4】:

      here是处理多个对象合并(两个以上对象)的解决方案:

      这是一个基于 angular.extend 函数的 extendDeep 函数。如果你把它添加到你的 $scope 中,你就可以调用

      $scope.meta = $scope.extendDeep(ajaxResponse1.myMeta, ajaxResponse2.defaultMeta);
      

      并获得您正在寻找的答案。

      $scope.extendDeep = function extendDeep(dst) {
        angular.forEach(arguments, function(obj) {
          if (obj !== dst) {
            angular.forEach(obj, function(value, key) {
              if (dst[key] && dst[key].constructor && dst[key].constructor === Object) {
                extendDeep(dst[key], value);
              } else {
                dst[key] = value;
              }     
            });   
          }
        });
        return dst;
      };
      

      【讨论】:

      • PS:这个函数的副作用是将后面的参数值复制到前面的参数中。
      【解决方案5】:

      如果您使用的是

      您可以使用 lodash 内置的 _.merge(),其功能与 angular > 1.4 版本相同

      因为lodash 已经在有角的人中很受欢迎,所以让我免于编写新函数

      【讨论】:

        猜你喜欢
        • 2018-02-27
        • 1970-01-01
        • 2018-09-21
        • 1970-01-01
        • 2023-04-02
        • 2021-01-15
        • 2017-12-14
        • 2014-05-02
        • 2018-03-30
        相关资源
        最近更新 更多