Родной способ слияния объектов в Javascript - PullRequest
47 голосов
/ 20 января 2011

Объект Javascript не имеет никакой встроенной операции слияния.Если у вас есть два объекта, скажите

{a:1, b:2}
{c:3, d:4}

и хотите получить

{a:1, b:2, c:3, d:4}

Насколько я знаю, вам придется перебирать объекты.Это означает, что вы выбираете либо стратегию слияния влево, либо стратегию слияния справа, а затем делаете что-то вроде (упрощенно)

for (key in object2) {
  object1[key] = object2[key];
}

Это нормально.Тем не менее, Javascript имеет функции call и prototype.Например, превращение arguments в Array может быть выполнено с помощью

Array.prototype.slice.call(arguments)

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

Вопрос

Есть ли хитрость в использовании этого прототипа / шаблона вызова, возможно, для функций обхода Attribute или Node DOM, иливозможно, некоторые из общих String функций для слияния нативных объектов?

Код будет выглядеть примерно так:

var merged = somethingrandom.obscuremethod.call(object1, object2)

И в результате вы получите нативное слияние без обхода.

Возможное, неоптимальное решение

Если бы вы могли использовать свойство constructor Object, а затем заставить один объект создать конструктор другого объекта, а затем запустить new над составным объектомВы можете получить слияние бесплатно.Но у меня нет четкого понимания всех последствий использования функции constructor в javascript для выполнения этого вызова.

Лемма

Тот же вопрос справедлив для Arrays.Общая проблема состоит в том, чтобы взять, скажем, 7 массивов чисел, а затем попытаться выяснить пересечение этих массивов.То есть, какие числа существуют во всех 7 массивах.

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

Есть мысли?

edit:

На полпутиТам

Для решения проблемы с массивом вы можете сделать следующее:

array.concat (a, b, c) .sort (). join (':') и затем использовать некоторые хитрыеRegExp захват и повторение паттернов для прохождения.Реализации RegExp, если вы не знаете, работают на очень простой виртуальной машине, основанной на стеке.Когда вы инициализируете свое регулярное выражение, это действительно программа, которая компилируется (RegExp.compile является устаревшим методом JS).Затем натив перебегает через струну невероятно быстро.Возможно, вы могли бы использовать это для порогов членства и получить лучшую производительность ...

Это все еще не проходит весь путь.

Ответы [ 7 ]

24 голосов
/ 20 января 2011

Мой ответ на этот вопрос будет разочаровывающим, но все же:

нет

Причина этого проста: реализация слияния (или «расширение» г-на Ресига)как это называется для объектов) в jQuery делает цикл, как и тот, что в вашем вопросе.Вы можете посмотреть на это здесь .И я смею сказать, что если Джон Резиг не нашел умного встроенного способа сделать это, то простые смертные из stackoverflow тоже не будут:)

3 голосов
/ 06 ноября 2015

Вопрос на миллион долларов!Я пытался сделать это многочисленными способами, и описанный выше способ петли всегда казался самым грязным.Object.setPrototypeOf() ES6 позволяет вам делегировать объект "свойства переопределения" объекту "свойств по умолчанию", в значительной степени выполняя то, что вы пытаетесь сделать, но использование Object.setPrototypeOf() имеет некоторые серьезные последствия, такие как отключение оптимизации компилятора браузера длявесь сценарий.

Кроме того, как в решении цикла, так и в решении Object.setPrototypeOf() вы остаетесь в ситуации, когда объект "переопределение свойств" может изменять объект "свойства по умолчанию":

defaultObj = {
    a: [1, 2]
}
...
overrideObj = {
    b: 3
}
Object.setPrototypeOf(overrideObj, defaultObj);
console.log(overrideObj); // {a: [1, 2], b: 3}
// Great!
...
overrideObj.a.push(4);
console.log(defaultObj); // {a: [1, 2, 4]}
// Uh-oh.

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

Лучшим решением может быть использование JSON.stringify и JSON.parse для копирования и объединения объектов.Вот суть с примером: https://gist.github.com/spikesagal/6f7822466887f19b9c65

HTH

2 голосов
/ 11 августа 2017

Используя ES6 (ES2015) вы можете использовать Object.assign метод:

var x = {a:1, b:2};
var y = {c:3, d:4};
var z = Object.assign({},x,y);

Используя ES7 (ES2016, Chrome 60+ или Babel) вы можете использовать Распространение объектовоператор :

var x = {a:1, b:2};
var y = {c:3, d:4}; 
var z = {...x, ...y};
2 голосов
/ 20 января 2011

Не то, что я знаю, нет. Также вы захотите написать свой метод слияния следующим образом:

function mergeInto(o1, o2) {
  if (o1 == null || o2 == null)
    return o1;

  for (var key in o2)
    if (o2.hasOwnProperty(key))
      o1[key] = o2[key];

  return o1;
}
1 голос
/ 15 августа 2013

Вы можете сделать следующее, используя собственный JS 1.7, без необходимости в фреймворке.См. Пример на fiddle (пример предназначен только для простых объектов - не сложных вложенных объектов)

var obj1 = {a: "a", b: "b"};
var obj2 = {c: "c", d: "d"};

// The magic: ugly but works perfectly
var value = (JSON.stringify(obj1).concat(JSON.stringify(obj2))).replace("}{", ",");

document.getElementById("lbl1").setAttribute("value", value);

// back to object
var obj3 = JSON.parse(value);
document.getElementById("lbl2").setAttribute("value", obj3.a + " " + obj3.b + " " + obj3.c + " " + obj3.d);
0 голосов
/ 20 октября 2016

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

var merge = function(o1, o2) {
    var o_new = {};
    for(p in o1) {
        if(o1[p]) {
            if(typeof o1[p] == 'object' && !(o1[p] instanceof Array) && o2.hasOwnProperty(p)) {
                o_new[p] = merge(o1[p], o2[p]);
            }
            else {
                o_new[p] = o1[p];
            }
        }
    }
    for(p in o2) {
        if(typeof o2[p] == 'object' && !(o2[p] instanceof Array) && o1.hasOwnProperty(p)) {
            o_new[p] = merge(o1[p], o2[p]);
        }
        else {
            o_new[p] = o2[p];
        }
    }
    return o_new;
}
0 голосов
/ 07 апреля 2014

Нет встроенных способов в ECMA-Script, используйте:

function merge(o1,o2) {
 if (typeof(o1)!=='object') o1={};
 if (typeof(o2)!=='object') o2={};
 for (var k in o2) {
 if (o1[k]!==undefined)
  alert ('Collision Error'); // TODO
 else
   o1[k]=o2[k];
 }
 return o1;
}
...