【问题标题】:Implement custom behavior for Object.assign为 Object.assign 实现自定义行为
【发布时间】:2020-02-15 11:59:56
【问题描述】:

有没有办法为 Object.assign 实现自定义行为 - 特别是我希望合并而不是覆盖数组类型属性值 - 实现它的最简单方法是什么?

例子:

默认行为:

Object.assign({a:[1]}, {a:[2]}).a // [2]

而我想要的结果是[1,2]

【问题讨论】:

  • 你想要的是深度合并。我个人会为此使用 deepmerge 或 lodash 包。
  • @DillanWilding 谢谢你,我正在学习 es6 - 试图远离任何自定义库 - 但感谢你的建议。我对 Object.assign 感到 99% 的满意——它可以很好地深度合并对象,唯一的一点是它处理数组的方式。
  • AFAIK 深度合并完全替换了一个数组
  • It deep merges object just fine 不,它没有,它执行浅合并。
  • 我很确定 Object.assign 无法自定义它的合并方式,因此您必须手动迭代键并测试类型

标签: javascript ecmascript-6 javascript-objects


【解决方案1】:

我不知道Object.assign,但我过去需要它并像这样实现它(使用lodash

import _f from 'lodash/fp'; // functional lodash interface

export const mergeAllLeaveArrays = _f.mergeAllWith((objValue, srcValue) => {
  if (_f.isArray(srcValue)) return srcValue;
});

// This is what you want
export const mergeAllConcatArray = _f.mergeAllWith((objValue, srcValue) => {
  if (_f.isArray(srcValue) && (objValue == null || _f.isArray(objValue)))
    return _f.concat(objValue || [], srcValue);
});

我从我的实用程序库中复制并粘贴了它,我正在使用功能性lodash 接口。如果您更喜欢它,应该直接将其转换为命令式 lodash 接口。

【讨论】:

    【解决方案2】:

    是的,可以覆盖Object.assign,但不建议这样做,因为这样做时,您会更改全局标准 js 函数,这可能会导致使用 Object.assign 的外部 JS 库停止工作,或者它们将继续工作错误的方式(因此这可能会阻止您使用外部库,或阻止其他人使用您的代码)。

    相反,只需定义新功能即可满足您的需求,例如(这是示例 - 不是丰富的深层对象合并)

    function ObjectAssign(a,b) {
      let o={};
      for(let k of Object.keys(a)) {
        o[k] = a[k];
        o[k] = b[k] ? a[k].concat(b[k]) : b[k];
      }
      return o;
    }
    
    result = ObjectAssign({a:[1]}, {a:[2]});
    console.log(result);

    【讨论】:

      【解决方案3】:

      我正在创建自定义函数,而不是将分配原型附加到 Object 类以合并对象。这是我不使用 lodash 可以获得的最简单的解决方案。

      function mergeObject(srcObj, targetObj) {
        // Get all unique keys
        const aSet = new Set([...Object.keys(srcObj), ...Object.keys(targetObj)])
        const mergedObject = {}
        aSet.forEach(k => {
          // Verify if both end of key is an array
          if (Array.isArray(srcObj[k]) && Array.isArray(targetObj[k])) {
            // Concat given array
            mergedObject[k] = srcObj[k].concat(targetObj[k])
          } else {
            // assign target value if present or source value
            mergedObject[k] = targetObj[k] || srcObj[k]
          }
        });
        return mergedObject;
      }
      

      此解决方案也仅适用于 es6。

      【讨论】:

        【解决方案4】:

        您不会经常看到它,但是在不修改内置库、将功能附加到对象本身或使用替代 API 的情况下更改赋值行为的一种干净方法是将目标包装在Proxy。但请注意,Object.assign 也会在此处返回代理引用 - 您可能希望忘记它并继续使用原始对象。

        const arrayMergeProxy = input => new Proxy(input, {
            set(target, prop, newValue) {
                const currentValue = Reflect.get(target, prop)
                return Reflect.set(
                    target, 
                    prop,
                    Array.isArray(currentValue) && Array.isArray(newValue)
                        ? currentValue.concat(newValue)
                        : newValue
                )
            }
        })
        
        const o1 = { a:[1], b: false }
        const o2 = { a:[2], b: true }
        Object.assign(arrayMergeProxy(o1), o2)
        
        console.log(o1)

        【讨论】:

          【解决方案5】:

          Object.defineProperty(Object, "assignMerge", {
          		value: function assign(target, varArgs) {
          			'use strict';
          			if (target == null) {
          				throw new TypeError('Cannot convert undefined or null to object');
          			}
          
          			var to = Object(target);
          
          			for (var index = 1; index < arguments.length; index++) {
          				var nextSource = arguments[index];
          
          				if (nextSource != null) {
          					for (var nextKey in nextSource) {
          						if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                        if(to.hasOwnProperty(nextKey) && nextSource.hasOwnProperty(nextKey) && Array.isArray(to[nextKey]) && Array.isArray(nextSource[nextKey])){
                          to[nextKey] = to[nextKey].concat(nextSource[nextKey])
                        }
          						}
          					}
          				}
          			}
          			return to;
          		},
          		writable: true,
          		configurable: true
          });
          
          const mergedObjects = Object.assignMerge({a:[1, 2], b:[2], c:[2]}, {a:[2,5], b:[1]});
          console.log(mergedObjects)

          我认为,如果您正在创建自己的方法并应用与 Object.assign polyfill 相同的原则,它可能看起来像这样。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-07-20
            • 1970-01-01
            • 2014-06-06
            • 1970-01-01
            • 2015-09-07
            • 1970-01-01
            • 2022-10-04
            • 2011-05-05
            相关资源
            最近更新 更多