Оператор распространения ecmascript6 не работает так, как ожидалось, для объектов javascript [duplicate]

Хотя в настоящее время в стандартном CSS нет родительского селектора, я работаю над (личным) проектом с именем ax (т. е. расширенный синтаксис селектора CSS / ACSSSS ), который среди семи новых селекторов , включает в себя:

  1. селектор непосредственного родителя < (который позволяет сделать противоположный выбор >)
  2. a любой предка селектор ^ (который позволяет сделать противоположный выбор [SPACE])

ax в настоящее время на относительно ранней стадии разработки BETA.

См. Демонстрацию здесь:

http://rounin.co.uk/projects/axe/axe2.html

(сравните два списка на в левом стиле со стандартными селекторами и двумя списками справа в стиле с селекторами топора)

208
задан Braiam 4 November 2016 в 18:18
поделиться

27 ответов

Кто-нибудь знает, существует ли глубокое слияние в спецификации ES6 / ES7?

Нет, это не так.

240
ответ дан user 19 August 2018 в 05:04
поделиться
  • 1
    Просмотрите историю изменений. В то время, когда я ответил на этот вопрос, вопрос был Кто-нибудь знает, существует ли глубокое слияние в спецификации ES6 / ES7? . – user 28 July 2017 в 12:11

Мы можем использовать $ .extend (true, object1, object2) для глубокого слияния. Значение true обозначает объединение двух объектов рекурсивно, изменение первого.

$ extend (true, target, object)

2
ответ дан Abinaya 19 August 2018 в 05:04
поделиться
  • 1
    Ассистент никогда не указывал, что они используют jquery и, похоже, запрашивают собственное решение для JavaScript. – Teh JoE 27 March 2017 в 15:31
  • 2
    Это очень простой способ сделать это, и он работает. Жизнеспособное решение, которое я бы рассмотрел, если бы я был тем, кто задавал этот вопрос. :) – kashiraja 8 December 2017 в 19:40

Есть хорошо сохранившиеся библиотеки, которые уже делают это. Одним из примеров в реестре npm является слияние

0
ответ дан AliAvci 19 August 2018 в 05:04
поделиться

Здесь реализована реализация TypeScript:

export const mergeObjects = <T extends object = object>(target: T, ...sources: T[]): T  => {
  if (!sources.length) {
    return target;
  }
  const source = sources.shift();
  if (source === undefined) {
    return target;
  }

  if (isMergebleObject(target) && isMergebleObject(source)) {
    Object.keys(source).forEach(function(key: string) {
      if (isMergebleObject(source[key])) {
        if (!target[key]) {
          target[key] = {};
        }
        mergeObjects(target[key], source[key]);
      } else {
        target[key] = source[key];
      }
    });
  }

  return mergeObjects(target, ...sources);
};

const isObject = (item: any): boolean => {
  return item !== null && typeof item === 'object';
};

const isMergebleObject = (item): boolean => {
  return isObject(item) && !Array.isArray(item);
};

И модульные тесты:

describe('merge', () => {
  it('should merge Objects and all nested Ones', () => {
    const obj1 = { a: { a1: 'A1'}, c: 'C', d: {} };
    const obj2 = { a: { a2: 'A2'}, b: { b1: 'B1'}, d: null };
    const obj3 = { a: { a1: 'A1', a2: 'A2'}, b: { b1: 'B1'}, c: 'C', d: null};
    expect(mergeObjects({}, obj1, obj2)).toEqual(obj3);
  });
  it('should behave like Object.assign on the top level', () => {
    const obj1 = { a: { a1: 'A1'}, c: 'C'};
    const obj2 = { a: undefined, b: { b1: 'B1'}};
    expect(mergeObjects({}, obj1, obj2)).toEqual(Object.assign({}, obj1, obj2));
  });
  it('should not merge array values, just override', () => {
    const obj1 = {a: ['A', 'B']};
    const obj2 = {a: ['C'], b: ['D']};
    expect(mergeObjects({}, obj1, obj2)).toEqual({a: ['C'], b: ['D']});
  });
  it('typed merge', () => {
    expect(mergeObjects<TestPosition>(new TestPosition(0, 0), new TestPosition(1, 1)))
      .toEqual(new TestPosition(1, 1));
  });
});

class TestPosition {
  constructor(public x: number = 0, public y: number = 0) {/*empty*/}
}
7
ответ дан Andrew 19 August 2018 в 05:04
поделиться

Вы можете использовать слияние Lodash :

var object = {
  'a': [{ 'b': 2 }, { 'd': 4 }]
};

var other = {
  'a': [{ 'c': 3 }, { 'e': 5 }]
};

_.merge(object, other);
// => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
50
ответ дан AndrewHenderson 19 August 2018 в 05:04
поделиться
  • 1
    Эй, люди, это самое простое и красивое решение. Lodash является удивительным, они должны включать его в качестве основного объекта js – Nurbol Alpysbayev 2 February 2018 в 09:32
  • 2
    Очень чистый! Спасибо – Elron 27 June 2018 в 03:13
  • 3
    Не должен ли быть результат { 'a': [{ 'b': 2 }, { 'c': 3 }, { 'd': 4 }, { 'e': 5 }] }? – J. Hesters 16 August 2018 в 11:34
  • 4
    Хороший вопрос. Это может быть отдельный вопрос или один для сторонников Lodash. – AndrewHenderson 17 August 2018 в 18:04
  • 5
    – Alexandru Furculita 25 September 2018 в 09:12
function isObject(obj) {
    return obj !== null && typeof obj === 'object';
}
const isArray = Array.isArray;

function isPlainObject(obj) {
    return isObject(obj) && (
        obj.constructor === Object  // obj = {}
        || obj.constructor === undefined // obj = Object.create(null)
    );
}

function mergeDeep(target, ...sources){
    if (!sources.length) return target;
    const source = sources.shift();

    if (isPlainObject(source) || isArray(source)) {
        for (const key in source) {
            if (isPlainObject(source[key]) || isArray(source[key])) {
                if (isPlainObject(source[key]) && !isPlainObject(target[key])) {
                    target[key] = {};
                }else if (isArray(source[key]) && !isArray(target[key])) {
                    target[key] = [];
                }
                mergeDeep(target[key], source[key]);
            } else if (source[key] !== undefined && source[key] !== '') {
                target[key] = source[key];
            }
        }
    }

    return mergeDeep(target, ...sources);
}

// test...
var source = {b:333};
var source2 = {c:32, arr: [33,11]}
var n = mergeDeep({a:33}, source, source2);
source2.arr[1] = 22;
console.log(n.arr); // out: [33, 11]
0
ответ дан asins 19 August 2018 в 05:04
поделиться

Я хотел бы представить довольно простой вариант ES5. Функция получает 2 параметра - target и source, которые должны быть типа «объект». Target будет результирующим объектом. Target сохраняет все свои исходные свойства, но их значения могут быть изменены.

function deepMerge(target, source) {
if(typeof target !== 'object' || typeof source !== 'object') return false; // target or source or both ain't objects, merging doesn't make sense
for(var prop in source) {
  if(!source.hasOwnProperty(prop)) continue; // take into consideration only object's own properties.
  if(prop in target) { // handling merging of two properties with equal names
    if(typeof target[prop] !== 'object') {
      target[prop] = source[prop];
    } else {
      if(typeof source[prop] !== 'object') {
        target[prop] = source[prop];
      } else {
        if(target[prop].concat && source[prop].concat) { // two arrays get concatenated
          target[prop] = target[prop].concat(source[prop]);
        } else { // two objects get merged recursively
          target[prop] = deepMerge(target[prop], source[prop]); 
        } 
      }  
    }
  } else { // new properties get added to target
    target[prop] = source[prop]; 
  }
}
return target;
}

случаи:

  • , если target не имеет source свойство target получает его;
  • , если target имеет свойство source и target & amp; source не являются обеими объектами (3 случая из 4), свойство target получает overriden;
  • , если target имеет свойство source, и оба они являются объектами / массивами (1 оставшийся случай), тогда рекурсия объединяет два объекта (или объединение двух массивов),

также учитывает следующее:

  1. array + obj = array
  2. obj + array = obj
  3. obj + obj = obj (рекурсивно объединенный)
  4. array + array = array (concat)

Предсказуемо, поддерживает примитивные типы, а также массивы и объекты. Кроме того, поскольку мы можем объединить 2 объекта, я думаю, что мы можем объединить более двух с помощью функции reduce .

взглянуть на пример (и поиграть с ним, если хотите ):

var a = {
   "a_prop": 1,
   "arr_prop": [4, 5, 6],
   "obj": {
     "a_prop": {
       "t_prop": 'test'
     },
     "b_prop": 2
   }
};

var b = {
   "a_prop": 5,
   "arr_prop": [7, 8, 9],
   "b_prop": 15,
   "obj": {
     "a_prop": {
       "u_prop": false
     },
     "b_prop": {
        "s_prop": null
     }
   }
};

function deepMerge(target, source) {
    if(typeof target !== 'object' || typeof source !== 'object') return false;
    for(var prop in source) {
    if(!source.hasOwnProperty(prop)) continue;
      if(prop in target) {
        if(typeof target[prop] !== 'object') {
          target[prop] = source[prop];
        } else {
          if(typeof source[prop] !== 'object') {
            target[prop] = source[prop];
          } else {
            if(target[prop].concat && source[prop].concat) {
              target[prop] = target[prop].concat(source[prop]);
            } else {
              target[prop] = deepMerge(target[prop], source[prop]); 
            } 
          }  
        }
      } else {
        target[prop] = source[prop]; 
      }
    }
  return target;
}

console.log(deepMerge(a, b));

Существует ограничение - длина стека вызовов браузера. Современные браузеры выдадут ошибку на каком-то действительно глубоком уровне рекурсии (подумайте о тысячах вложенных вызовов). Кроме того, вы можете свободно обращаться с ситуациями типа array + object и т. Д., Добавив новые условия и проверки типов.

2
ответ дан curveball 19 August 2018 в 05:04
поделиться

Кто-нибудь знает, существует ли глубокое слияние в спецификации ES6 / ES7?

Документация Object.assign предполагает, что он не делает глубокого клонирования .

0
ответ дан Gaurang Patel 19 August 2018 в 05:04
поделиться

Поскольку этот вопрос все еще активен, вот еще один подход:

  • ES6 / 2015
  • Неизменяемый (не изменяет исходные объекты)
  • Ручки массивы (объединяет их)

/**
* Performs a deep merge of objects and returns new object. Does not modify
* objects (immutable) and merges arrays via concatenation.
*
* @param {...object} objects - Objects to merge
* @returns {object} New object with merged key/values
*/
function mergeDeep(...objects) {
  const isObject = obj => obj && typeof obj === 'object';
  
  return objects.reduce((prev, obj) => {
    Object.keys(obj).forEach(key => {
      const pVal = prev[key];
      const oVal = obj[key];
      
      if (Array.isArray(pVal) && Array.isArray(oVal)) {
        prev[key] = pVal.concat(...oVal);
      }
      else if (isObject(pVal) && isObject(oVal)) {
        prev[key] = mergeDeep(pVal, oVal);
      }
      else {
        prev[key] = oVal;
      }
    });
    
    return prev;
  }, {});
}

// Test objects
const obj1 = {
  a: 1,
  b: 1, 
  c: { x: 1, y: 1 },
  d: [ 1, 1 ]
}
const obj2 = {
  b: 2, 
  c: { y: 2, z: 2 },
  d: [ 2, 2 ],
  e: 2
}
const obj3 = mergeDeep(obj1, obj2);

// Out
console.log(obj3);

12
ответ дан jhildenbiddle 19 August 2018 в 05:04
поделиться
  • 1
    Это мило. Однако, когда у нас есть массив с повторяющимися элементами, они объединены (есть повторяющиеся элементы). Я адаптировал это для принятия параметра (массивы уникальные: true / false). – Astronaut 7 June 2018 в 13:46
  • 2
    Чтобы сделать массивы уникальными, вы можете изменить prev[key] = pVal.concat(...oVal); на prev[key] = [...pVal, ...oVal].filter((element, index, array) => array.indexOf(element) === index); – Richard Herries 12 June 2018 в 13:35

У меня возникла эта проблема при загрузке состояния кэшированного сокращения. Если я просто загружаю состояние кеширования, я бы столкнулся с ошибками для новой версии приложения с обновленной структурой состояния.

Уже упоминалось, что lodash предлагает функцию merge, которую я использовал:

const currentInitialState = configureState().getState();
const mergedState = _.merge({}, currentInitialState, cachedState);
const store = configureState(mergedState);
3
ответ дан mBeierl 19 August 2018 в 05:04
поделиться

Вот еще один, который я только написал, что поддерживает массивы. Он конкатцирует их.

function isObject(obj) {
    return obj !== null && typeof obj === 'object';
}


function isPlainObject(obj) {
    return isObject(obj) && (
        obj.constructor === Object  // obj = {}
        || obj.constructor === undefined // obj = Object.create(null)
    );
}

function mergeDeep(target, ...sources) {
    if (!sources.length) return target;
    const source = sources.shift();

    if(Array.isArray(target)) {
        if(Array.isArray(source)) {
            target.push(...source);
        } else {
            target.push(source);
        }
    } else if(isPlainObject(target)) {
        if(isPlainObject(source)) {
            for(let key of Object.keys(source)) {
                if(!target[key]) {
                    target[key] = source[key];
                } else {
                    mergeDeep(target[key], source[key]);
                }
            }
        } else {
            throw new Error(`Cannot merge object with non-object`);
        }
    } else {
        target = source;
    }

    return mergeDeep(target, ...sources);
};
2
ответ дан mpen 19 August 2018 в 05:04
поделиться

Это не существует, но вы можете использовать JSON.parse(JSON.stringify(jobs))

0
ответ дан PaRoJa 19 August 2018 в 05:04
поделиться
  • 1
    Это приведет к глубокому копированию / клонированию объекта, но не к объединению двух объектов. – Mike Scotty 15 March 2017 в 09:45
  • 2
    Это не будет копировать методы из одного объекта в другой, поскольку JSON не поддерживает тип функции – danwellman 3 May 2017 в 21:05
  • 3
    Если клонирование функции не является проблемой, вы можете сделать что-то вроде ... Object.assign ({first: 'object'}, JSON.parse (JSON.stringify ({second: 'object'}))); – FredArters 17 August 2017 в 14:51

Вот еще одно решение ES6, работает с объектами и массивами.

function deepMerge(...sources) {
  let acc = {}
  for (const source of sources) {
    if (source instanceof Array) {
      if (!(acc instanceof Array)) {
        acc = []
      }
      acc = [...acc, ...source]
    } else if (source instanceof Object) {
      for (let [key, value] of Object.entries(source)) {
        if (value instanceof Object && key in acc) {
          value = deepMerge(acc[key], value)
        }
        acc = { ...acc, [key]: value }
      }
    }
  }
  return acc
}
3
ответ дан pravdomil 19 August 2018 в 05:04
поделиться

Я знаю, что уже есть много ответов и столько комментариев, что они не сработают. Единственный консенсус в том, что он настолько сложный, что никто не сделал для него стандарта . Однако большинство принятых ответов в SO раскрывают «простые трюки», которые широко используются. Итак, для всех нас, как и я, которые не являются экспертами, но хотят писать более безопасный код, хватая немного больше на сложность javascript, я попытаюсь пролить некоторый свет.

Прежде чем загрязнять руки, пусть я уточняю 2 пункта:

  • [ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ] Я предлагаю функцию ниже, которая решает, как мы глубокий цикл в объекты javascript для копирования и иллюстрирует что, как правило, слишком кратко комментируется. Это не готово к производству. Ради ясности, я purposedly оставить в стороне других соображения, такие как круговые объекты (дорожки с помощью набора или unconflicting свойства символа) , копирование опорного значения или глубокого клона , неизменное объект назначения (глубокий клон снова?), в каждом конкретном случае исследование каждого типа объектов , get / set свойства через accessors ... Кроме того, я не тестировал производительность - хотя это важно, потому что здесь дело не в этом.
  • Я использую copy или присваивать термины вместо merge . Потому что, по моему мнению, слияние является консервативным и должно терпеть неудачу при конфликтах. Здесь, когда мы конфликтуем, мы хотим, чтобы источник перезаписывал адресат. Как Object.assign делает.

Ответы с for..in или Object.keys вводят в заблуждение

Создание глубокой копии кажется такой базовой и обычной практикой, которую мы ожидаем найти однострочный или, по крайней мере, быстрый выигрыш посредством простой рекурсии. Мы не ожидаем, что нам понадобится библиотека или напишите пользовательскую функцию из 100 строк.

Когда я впервые прочитал ответ Салакара , я искренне думал, что смогу сделать лучше и проще ( вы можете сравнить его с Object.assign на x={a:1}, y={a:{b:1}}). Затем я прочитал ответ the8472 , и я подумал ... нет так легко уйти, что улучшение уже предоставленных ответов не заставит нас далеко.

Давайте дадим глубокую копию и рекурсивную в одно мгновение. Просто подумайте, как (ошибочно) люди анализируют свойства для копирования очень простого объекта.

const y = Object.create(
    { proto : 1 },
    { a: { enumerable: true, value: 1},
      [Symbol('b')] : { enumerable: true, value: 1} } )

Object.assign({},y)
> { 'a': 1, Symbol(b): 1 } // All (enumerable) properties are copied

((x,y) => Object.keys(y).reduce((acc,k) => Object.assign(acc, { [k]: y[k] }), x))({},y)
> { 'a': 1 } // Missing a property!

((x,y) => {for (let k in y) x[k]=y[k];return x})({},y)
> { 'a': 1, 'proto': 1 } // Missing a property! Prototype's property is copied too!

Object.keys будет опускать собственные неперечислимые свойства, собственные свойства с символьными ключами и все свойства прототипа. Это может быть хорошо, если у ваших объектов нет таких. Но имейте в виду, что Object.assign обрабатывает собственные перечислимые свойства с символьными ключами. Таким образом, ваша пользовательская копия потеряла свой цвет.

for..in предоставит свойства источника, его прототипа и полной цепи прототипов, если вы этого не захотите (или зная об этом).

Если вы пишете функцию общего назначения, и вы не используете Object.getOwnPropertyDescriptors, Object.getOwnPropertyNames, Object.getOwnPropertySymbols, вы можете использовать слишком много свойств, смешивая свойства прототипа и собственные свойства. ] или Object.getPrototypeOf, вы, скорее всего, ошибаетесь.

Что следует учитывать перед написанием вашей функции

Сначала убедитесь, что вы понимаете, что такое объект Javascript. В Javascript объект создается из его собственных свойств и (родительского) объекта прототипа. Объект прототипа, в свою очередь, состоит из его собственных свойств и прототипа. И т. Д., Определяя цепочку прототипов.

Свойство представляет собой пару ключей (string или symbol) и дескрипторов (value или get / set и атрибутов например enumerable).

Наконец, существует множество типов объектов . Вы можете захотеть иначе обрабатывать объект Object с объекта Date или объекта Function.

Итак, написав свою глубокую копию, вы должны ответить хотя бы на эти вопросы:

  1. Что я считаю глубоким (правильным для рекурсивного поиска) или плоским?
  2. Какие свойства я хочу скопировать? (перечисляемые / неперечислимые, строковые или с символьными ключами, собственные свойства / собственные свойства прототипа, значения / дескрипторы ...)

В моем примере я считаю, что только object Object s являются deep , потому что другие объекты, созданные другими конструкторами, могут быть неадекватными для углубленного просмотра. this []

function toType(a) {
    // Get fine type (object, array, function, null, error, date ...)
    return ({}).toString.call(a).match(/([a-z]+)(:?\])/i)[1];
}

function isDeepObject(obj) {
    return "Object" === toType(obj);
}

И я сделал объект options, чтобы выбрать, что копировать (для демонстрационной цели).

const options = {nonEnum:true, symbols:true, descriptors: true, proto:true};

Предлагаемая функция

Вы можете протестировать ее в этом plunker .

function deepAssign(options) {
    return function deepAssignWithOptions (target, ...sources) {
        sources.forEach( (source) => {

            if (!isDeepObject(source) || !isDeepObject(target))
                return;

            // Copy source's own properties into target's own properties
            function copyProperty(property) {
                const descriptor = Object.getOwnPropertyDescriptor(source, property);
                //default: omit non-enumerable properties
                if (descriptor.enumerable || options.nonEnum) {
                    // Copy in-depth first
                    if (isDeepObject(source[property]) && isDeepObject(target[property]))
                        descriptor.value = deepAssign(options)(target[property], source[property]);
                    //default: omit descriptors
                    if (options.descriptors)
                        Object.defineProperty(target, property, descriptor); // shallow copy descriptor
                    else
                        target[property] = descriptor.value; // shallow copy value only
                }
            }

            // Copy string-keyed properties
            Object.getOwnPropertyNames(source).forEach(copyProperty);

            //default: omit symbol-keyed properties
            if (options.symbols)
                Object.getOwnPropertySymbols(source).forEach(copyProperty);

            //default: omit prototype's own properties
            if (options.proto)
                // Copy souce prototype's own properties into target prototype's own properties
                deepAssign(Object.assign({},options,{proto:false})) (// Prevent deeper copy of the prototype chain
                    Object.getPrototypeOf(target),
                    Object.getPrototypeOf(source)
                );

        });
        return target;
    }
}

Это можно использовать следующим образом:

const x = { a: { a: 1 } },
      y = { a: { b: 1 } };
deepAssign(options)(x,y); // { a: { a: 1, b: 1 } }
13
ответ дан RaphaMex 19 August 2018 в 05:04
поделиться
  • 1
    Отличный ответ +1, очень полезный для пользовательского глубокого слияния, над которым я работаю. – Fred Kleuver 4 March 2018 в 01:39

Есть ли способ сделать это?

Если библиотеки npm можно использовать в качестве решения, object-merge-advanced от вас действительно позволяет объединить объекты глубоко и настроить / переопределить каждое действие слияния с помощью знакомой функции обратного вызова. Основная идея этого - не просто глубокое слияние - что происходит со значением, когда два ключа одинаковы ? Эта библиотека позаботится об этом - при столкновении двух клавиш object-merge-advanced весит типы, стремясь сохранить как можно больше данных после слияния:

Ключ первого входного аргумента помечен # 1, второй аргумент - # 2. В зависимости от каждого типа один выбирается для значения ключа результата. В диаграмме «объект» означает простой объект (не массив и т. Д.).

Когда ключи не сталкиваются, все они вводят результат.

] Из вашего фрагмента примера, если вы использовали object-merge-advanced для объединения фрагмента кода:

const mergeObj = require("object-merge-advanced");
const x = { a: { a: 1 } };
const y = { a: { b: 1 } };
const res = console.log(mergeObj(x, y));
// => res = {
//      a: {
//        a: 1,
//        b: 1
//      }
//    }

Этот алгоритм рекурсивно пересекает все ключи входных объектов, сравнивает и строит и возвращает новый объединенный результат.

1
ответ дан revelt 19 August 2018 в 05:04
поделиться

Я знаю, что это немного старая проблема, но самое простое решение в ES2015 / ES6, которое я мог придумать, было на самом деле довольно простым, используя Object.assign (),

. Надеюсь, это поможет:

/**
 * Simple object check.
 * @param item
 * @returns {boolean}
 */
export function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item));
}

/**
 * Deep merge two objects.
 * @param target
 * @param ...sources
 */
export function mergeDeep(target, ...sources) {
  if (!sources.length) return target;
  const source = sources.shift();

  if (isObject(target) && isObject(source)) {
    for (const key in source) {
      if (isObject(source[key])) {
        if (!target[key]) Object.assign(target, { [key]: {} });
        mergeDeep(target[key], source[key]);
      } else {
        Object.assign(target, { [key]: source[key] });
      }
    }
  }

  return mergeDeep(target, ...sources);
}

Пример использования:

mergeDeep(this, { a: { b: { c: 123 } } });
// or
const merged = mergeDeep({a: 1}, { b : { c: { d: { e: 12345}}}});  
console.dir(merged); // { a: 1, b: { c: { d: [Object] } } }

Вы найдете неизменную версию этого в ответе ниже.

Обратите внимание, что это приведет к бесконечной рекурсии по циклическим ссылкам. Здесь есть несколько отличных ответов о том, как обнаружить циркулярные ссылки, если вы считаете, что столкнулись с этой проблемой.

109
ответ дан Rubens Mariuzzo 19 August 2018 в 05:04
поделиться

Иногда вам не требуется глубокое слияние, даже если вы так думаете. Например, если у вас есть конфигурация по умолчанию с вложенными объектами, и вы хотите расширить ее с помощью собственной конфигурации, вы можете создать для нее класс. Концепция очень проста:

function AjaxConfig(config) {

  // Default values + config

  Object.assign(this, {
    method: 'POST',
    contentType: 'text/plain'
  }, config);

  // Default values in nested objects

  this.headers = Object.assign({}, this.headers, { 
    'X-Requested-With': 'custom'
  });
}

// Define your config

var config = {
  url: 'https://google.com',
  headers: {
    'x-client-data': 'CI22yQEI'
  }
};

// Extend the default values with your own
var fullMergedConfig = new AjaxConfig(config);

// View in DevTools
console.log(fullMergedConfig);

Вы можете преобразовать ее в функцию (а не в конструктор).

0
ответ дан Ruslan 19 August 2018 в 05:04
поделиться

Если вы используете ImmutableJS , вы можете использовать mergeDeep:

fromJS(options).mergeDeep(options2).toJS();
6
ответ дан Steven Mercatante 19 August 2018 в 05:04
поделиться
  • 1
    у вас есть ошибка в вашем помощнике по фрагментам – Elise Chant 12 April 2017 в 05:16
  • 2
    @EliseChant Я так не думаю. Почему вы не уточняете? – BigDong 12 April 2017 в 10:12

Следующая функция делает глубокую копию объектов, она охватывает копирование примитива, массивы, а также объект

 function mergeDeep (target, source)  {
    if (typeof target == "object" && typeof source == "object") {
        for (const key in source) {
            if (source[key] === null && (target[key] === undefined || target[key] === null)) {
                target[key] = null;
            } else if (source[key] instanceof Array) {
                if (!target[key]) target[key] = [];
                //concatenate arrays
                target[key] = target[key].concat(source[key]);
            } else if (typeof source[key] == "object") {
                if (!target[key]) target[key] = {};
                this.mergeDeep(target[key], source[key]);
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}
4
ответ дан sudharsan tk 19 August 2018 в 05:04
поделиться

Проблема нетривиальна, если речь идет о объектах хоста или более сложном объекте, чем сумка значений

  • , вы вызываете геттер для получения значения или используете ли вы скопировать поверх дескриптора свойства?
  • что, если цель слияния имеет сеттер (либо собственное свойство, либо его цепочку прототипов)? Вы считаете значение уже существующим или вызываете установщик для обновления текущего значения?
  • вы вызываете функции собственной собственности или копируете их? Что, если они связаны функциями или функциями стрелок в зависимости от чего-то в своей цепочке видимости в момент их определения?
  • что, если это что-то вроде узла DOM? Вы, конечно, не хотите относиться к нему как к простому объекту и просто глубоко объединить все его свойства в
  • , как обращаться с «простыми» структурами, такими как массивы или карты или множества? Рассматривать их уже есть или слить их?
  • как бороться с неперечислимыми собственными свойствами?
  • как насчет новых поддеревьев?

Еще одна вещь, которую следует иметь в виду: графы объектов, которые содержат циклы. Обычно это не сложно - просто сохраните Set уже посещенных исходных объектов, но часто забывайте.

Вероятно, вы должны написать функцию глубокого слияния, которая ожидает только примитивные значения и простые объекты - в большинстве тех типов, которые алгоритм структурированного клона может обрабатывать - в качестве источников слияния. Бросьте, если он сталкивается с чем-либо, с которым он не может справиться или просто назначить по ссылке вместо глубокого слияния.

Другими словами, алгоритм одноразового использования не требуется, вам либо нужно катиться самостоятельно, либо смотреть для библиотечного метода, который распространяется на ваши прецеденты.

80
ответ дан the8472 19 August 2018 в 05:04
поделиться
  • 1
    Блестяще хорошее резюме проблем. – user 20 January 2016 в 04:18
  • 2
    оправдания для разработчиков V8, чтобы не реализовать безопасное «состояние документа». перевод – neaumusic 3 November 2016 в 19:21
  • 3
    Вы поднимаете много хороших вопросов, и мне было бы приятно видеть выполнение ваших рекомендаций. Поэтому я попытался сделать один из них ниже. Не могли бы вы посмотреть и прокомментировать? stackoverflow.com/a/48579540/8122487 – RaphaMex 3 February 2018 в 03:36

Пакет deepmerge npm, по-видимому, является наиболее широко используемой библиотекой для решения этой проблемы: https://www.npmjs.com/package/deepmerge

0
ответ дан user3336882 19 August 2018 в 05:04
поделиться

Я делаю этот метод для глубокого присваивания с помощью es6.

function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item) && item !== null)
}

function deepAssign(...objs) {
    if (objs.length < 2) {
        throw new Error('Need two or more objects to merge')
    }

    const target = objs[0]
    for (let i = 1; i < objs.length; i++) {
        const source = objs[i]
        Object.keys(source).forEach(prop => {
            const value = source[prop]
            if (isObject(value)) {
                if (target.hasOwnProperty(prop) && isObject(target[prop])) {
                    target[prop] = deepAssign(target[prop], value)
                } else {
                    target[prop] = value
                }
            } else if (Array.isArray(value)) {
                if (target.hasOwnProperty(prop) && Array.isArray(target[prop])) {
                    const targetArray = target[prop]
                    value.forEach((sourceItem, itemIndex) => {
                        if (itemIndex < targetArray.length) {
                            const targetItem = targetArray[itemIndex]

                            if (Object.is(targetItem, sourceItem)) {
                                return
                            }

                            if (isObject(targetItem) && isObject(sourceItem)) {
                                targetArray[itemIndex] = deepAssign(targetItem, sourceItem)
                            } else if (Array.isArray(targetItem) && Array.isArray(sourceItem)) {
                                targetArray[itemIndex] = deepAssign(targetItem, sourceItem)
                            } else {
                                targetArray[itemIndex] = sourceItem
                            }
                        } else {
                            targetArray.push(sourceItem)
                        }
                    })
                } else {
                    target[prop] = value
                }
            } else {
                target[prop] = value
            }
        })
    }

    return target
}
-1
ответ дан user6842 19 August 2018 в 05:04
поделиться

Вот неизменная (не изменяющая входы) версия ответа @ Салакара. Полезно, если вы делаете материал типа функционального программирования.

export function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item));
}

export default function mergeDeep(target, source) {
  let output = Object.assign({}, target);
  if (isObject(target) && isObject(source)) {
    Object.keys(source).forEach(key => {
      if (isObject(source[key])) {
        if (!(key in target))
          Object.assign(output, { [key]: source[key] });
        else
          output[key] = mergeDeep(target[key], source[key]);
      } else {
        Object.assign(output, { [key]: source[key] });
      }
    });
  }
  return output;
}
43
ответ дан WrongAboutMostThings 19 August 2018 в 05:04
поделиться
  • 1
    Что isObject? – user 11 May 2016 в 14:26
  • 2
    @torazaburo см. предыдущее сообщение от меня для функции isObject – Salakar 18 May 2016 в 20:45
  • 3
    Его имя вычисленного свойства, первое будет использовать значение key в качестве имени свойства, а позже будет «& quot; ключ & quot; имя свойства. См .: es6-features.org/#ComputedPropertyNames – CpILL 20 June 2016 в 13:16
  • 4
    в isObject вам не нужно проверять && item !== null в конце, потому что строка начинается с item &&, нет? – ephemer 14 November 2016 в 16:45
  • 5
    Если источник имеет вложенные дочерние объекты глубже мишеней, эти объекты будут по-прежнему ссылаться на те же значения в выходе mergedDeep (я думаю). Например. const target = { a: 1 }; const source = { b: { c: 2 } }; const merged = mergeDeep(target, source); merged.b.c; // 2 source.b.c = 3; merged.b.c; // 3 Это проблема? Он не мутирует входные данные, но любые будущие мутации для входов могут мутировать выход, и наоборот w / mutations для вывода мутирующих входов. Для чего это стоит, однако, R.merge() ramda имеет такое же поведение. – James Conkling 26 December 2016 в 21:03

Простое решение с ES5 (перезаписать существующее значение):

function merge(current, update) {
  Object.keys(update).forEach(function(key) {
    // if update[key] exist, and it's not a string or array,
    // we go in one level deeper
    if (current.hasOwnProperty(key) 
        && typeof current[key] === 'object'
        && !(current[key] instanceof Array)) {
      merge(current[key], update[key]);

    // if update[key] doesn't exist in current, or it's a string
    // or array, then assign/overwrite current[key] to update[key]
    } else {
      current[key] = update[key];
    }
  });
  return current;
}

var x = { a: { a: 1 } }
var y = { a: { b: 1 } }

console.log(merge(x, y));

2
ответ дан y.c 19 August 2018 в 05:04
поделиться

Если вы хотите избежать создания определенного метода, но все же нужно изменить глубокое значение объекта, не изменяя его, вы можете пойти так:

let obj = {a: {b: {c: false, other:''}, other: ''}, other: ''};
// making obj.a.b.c = true

let newObj = Object.assign(obj, {
  a: Object.assign(obj.a, {
    b: Object.assign(obj.a.b, {c: true})
  })
});

Не очень читаемо, но работает .

-3
ответ дан Augustin Riedinger 19 August 2018 в 05:04
поделиться
  • 1
    Это требует, чтобы вы знали все свойства заранее, и в этот момент вы могли бы также присвоить все свойства один за другим. – 3ocene 5 July 2017 в 20:38

Я попытался написать Object.assignDeep, который основан на пополнении Object.assign на mdn .

(ES5)

Object.assignDeep = function (target, varArgs) { // .length of function is 2
    'use strict';
    if (target == null) { // TypeError if undefined or 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) { // Skip over if undefined or null
            for (var nextKey in nextSource) {
                // Avoid bugs when hasOwnProperty is shadowed
                if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                    if (typeof to[nextKey] === 'object' 
                        && to[nextKey] 
                        && typeof nextSource[nextKey] === 'object' 
                        && nextSource[nextKey]) {                        
                        Object.assignDeep(to[nextKey], nextSource[nextKey]);
                    } else {
                        to[nextKey] = nextSource[nextKey];
                    }
                }
            }
        }
    }
    return to;
};
console.log(Object.assignDeep({},{a:{b:{c:1,d:1}}},{a:{b:{c:2,e:2}}}))

-1
ответ дан blackmiaool 19 August 2018 в 05:04
поделиться

Это просто и работает:

let item = {
    firstName: 'Jonnie',
    lastName: 'Walker',
    fullName: function fullName() {
            return 'Jonnie Walker';
    }
Object.assign(Object.create(item), item);

Объясните:

Object.create() Создает новый объект. Если вы передадите параметры для работы, он создаст объект с прототипом другого объекта. Поэтому, если у вас есть какие-либо функции на прототипе объекта, они будут переданы прототипу другого объекта.

Object.assign() Объединяет два объекта и создает полностью новый объект, и у них больше нет ссылки. Так что этот пример хорошо работает для меня.

-3
ответ дан Waqas Bukhary 19 August 2018 в 05:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: