Реализуйте пользовательское поведение для Object.assign - PullRequest
4 голосов
/ 18 октября 2019

Есть ли способ реализовать пользовательское поведение для Object.assign - в частности, я хочу, чтобы значения свойств типа массива были объединены, а не перезаписаны - какой самый простой способ добиться этого?

Пример:

Поведение по умолчанию:

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

, а желаемый результат для меня - [1,2].

Ответы [ 5 ]

2 голосов
/ 18 октября 2019

Я создаю пользовательскую функцию вместо того, чтобы прикреплять присвоить прототип к классу объектов для объединения объектов. Это самое простое решение, которое я могу получить без использования 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.

1 голос
/ 18 октября 2019

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, он может выглядеть следующим образом.

1 голос
/ 18 октября 2019

Вы не видите это слишком часто в дикой природе, но чистый способ изменения поведения присваивания без изменения встроенной библиотеки, присоединения функциональности к самому объекту или использования альтернативного 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)
1 голос
/ 18 октября 2019

Да, можно переопределить Object.assign, однако это не рекомендуется, потому что при этом вы меняете глобальную стандартную функцию js, которая может привести к тому, что внешние библиотеки JS, использующие Object.assign, могут прекратить работу или они будут работать внеправильный путь (так что это может заблокировать вас от использования внешних библиотек или запретить другим пользователям использовать ваш код).

Вместо этого просто определите новую функцию, которая делает то, что вам нужно, например (это пример - не слияние глубоких объектов)

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);
1 голос
/ 18 октября 2019

Я не знаю о 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, если вам это нравится больше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...