如何使用 immutablejs 合并两个对象?

How to do I merge two objects with immutablejs?

我想提供一个对象并从中创建一个克隆对象,但用新值覆盖而不影响原始对象。我想使用 immutablejs 来确保 javascript 对象是不可变的。

这是我的例子:

var map1 = Immutable.fromJS({
    selector:'DYNAMIC', 
    descendants:{
        icon:'x',           
        headingLink:'DYNAMIC',              
        text:'y',       
        readMoreLink:'z'    
    }
});

var map2 = map1.mergeDeep({
    selector:'NEW VALUE',
    descendants:{
        headingLink:'NEW VALUE'
    }
}).toObject();

我期待 map2 输出:

{
        selector:'NEW VALUE',   
        descendants:{
            icon:'x',           
            headingLink:'NEW VALUE',                
            text:'y',       
            readMoreLink:'z'    
        }
    }

相反,我得到了 ownerID 和 __altered 等的奇怪组合:

{
        selector:'NEW VALUE',   
        descendants:
            __altered: false,
            __hash:undefined,
            __ownerID:undefined,
            _root:pt,
            size:4
        }
    }

任何有助于完成这项工作的建议都将不胜感激。如果 immutablejs 不能做到这一点,那么也许有一个替代库可以。

toObject() 进行浅层转换,仅按下一个键。

要将 descendants Map 转换为普通 JS 对象,您必须执行以下操作:

map2.toObject().descendants.toObject()

不是很优雅,而且很容易出错。你可以只做 map2.toJS(),但如果处理大量数据,效率会很低。

理想情况下,我想知道您的用例是什么?您可以使用 Immutable.js 编写整个应用程序,而无需转换为普通 JavaScript 对象(与第 3 方组件和库一起使用除外)。这是一个功能强大的库,您计划对数据进行的任何转换都可以使用 Immutable 完成。

如果您只是在寻找不可变的 deepMerge,您可以使用 lodash 中的 _.merge,它甚至可以作为单独的模块使用。请注意,该函数中的第一个参数将发生变化(合并一个一个地应用于它),因此传入一个空对象,{},然后是你的合并源。

我提供了一个替代解决方案,使用纯 javascript。这适用于小物体。我不确定这会在多大程度上影响大型对象的性能,但它看起来真的很快并且几乎相同,如果不是比 lodash 快一毫秒的话。与 lodash 相比的优势在于它强制对象不可变。

var __ = {
  clone: function(obj){      
    var inst;
    if(Array.isArray(obj)){    

      inst = [];
      obj.forEach(function(item){
        inst.push(__.clone(item))
      });
    }else if(typeof obj === 'object'){            
      inst = {};
      for(key in obj){
        inst[key] = __.clone(obj[key]);
      }
      inst = Object.freeze(inst);
    }else{    
      inst = obj; // function, boolean, string, int, flt, date etc...
    }    
    // make this object immutable, so it cannot be changed.
    return inst;
  },
  recurseFreezeNew: function(objNew){
    var isArr = Array.isArray(objNew);
    if(!isArr && typeof objNew === 'object'){
      objNew = Object.freeze(objNew);
      for(key in objNew){
        objNew[key] = __.recurseFreezeNew(objNew[key]);
      }
    }else if(isArr){
            var self = this;
      objNew.forEach(function(item){
        item = __.recurseFreezeNew(item);
      });
    }
    return objNew;    
  },
  recurseMerge: function(objModel, objNew){
      var mergedResult;
      var key;

      var isModelArray = Array.isArray(objModel);
      var isNewArray = Array.isArray(objNew);

      var isModelObj = (!isModelArray && typeof objModel === 'object');
      var isNewObj = (!isNewArray && typeof objNew === 'object');

      var isBothObject = (isModelObj && isNewObj);    

      if(isBothObject){
        mergedResult = {};      
        for(key in objModel){
          if(!objNew[key]){
            mergedResult[key] = objModel[key]; //keep existing...
          }else{
            mergedResult[key] = __.recurseMerge(objModel[key], objNew[key]); // override new value
          }
        }

        for(key in objNew){
          if(!objModel[key]){
            mergedResult[key] = objNew[key]; // add new that don't exist...
          }
        }
        mergedResult = Object.freeze(mergedResult);
      }else{        
        objNew = __.recurseFreezeNew(objNew);        
        mergedResult = objNew;
      }            
      return mergedResult;
  }
};

var immutableMerge = function(){   
  var arr = Array.prototype.slice.call(arguments);
  var merged = __.clone(arr[0]);

  for(var i=1, intLen = arr.length; i < intLen; ++i){
    merged = __.recurseMerge(merged, arr[i]);
  }
  return merged;  
}


var merged = immutableMerge({
  'a':'default',
  'b':'default'
},{
  a:'NEW VALUE'
});

比较 immutablejs、lodash 和此自定义解决方案的示例: 查看控制台日志以查看 ms 差异。 8、3、1 毫秒速度。

https://jsfiddle.net/ejyvpbwx/14/