Питання Як я можу об'єднати властивості двох об'єктів JavaScript динамічно?


Мені потрібно мати можливість об'єднати два (дуже прості) об'єкти JavaScript під час виконання. Наприклад, я хотів би:

var obj1 = { food: 'pizza', car: 'ford' }
var obj2 = { animal: 'dog' }

obj1.merge(obj2);

//obj1 now has three properties: food, car, and animal

Хто-небудь має для цього скрипт або знає вбудований спосіб зробити це? Мені не потрібна рекурсія, і мені не потрібно об'єднувати функції, просто методи на плоских об'єктах.


1830


походження


Об'єкт.присвоєння .... - caub


Відповіді:


Стандартний метод ECMAScript 2018

Ви б використали об'єкт поширюється:

let merged = {...obj1, ...obj2};

/** There's no limit to the number of objects you can merge.
 *  Later properties overwrite earlier properties with the same name. */
const allRules = {...obj1, ...obj2, ...obj3};

Стандартний метод ECMAScript 2015 (ES6)

/* For the case in question, you would do: */
Object.assign(obj1, obj2);

/** There's no limit to the number of objects you can merge.
 *  All objects get merged into the first object. 
 *  Only the object in the first argument is mutated and returned.
 *  Later properties overwrite earlier properties with the same name. */
const allRules = Object.assign({}, obj1, obj2, obj3, etc);

(побачити MDN JavaScript Reference)


Метод ES5 і раніше

for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }

Зауважте, що це просто додати всі атрибути obj2 до obj1 який може бути не те, що ви хочете, якщо ви все ще хочете використовувати незмінених obj1.

Якщо ви використовуєте структуру, яка копіює всі ваші прототипи, то вам потрібно отримати фантаста з перевірками, як hasOwnProperty, але цей код буде працювати в 99% випадків.

Приклад функції:

/**
 * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
 * @param obj1
 * @param obj2
 * @returns obj3 a new object based on obj1 and obj2
 */
function merge_options(obj1,obj2){
    var obj3 = {};
    for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
    for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
    return obj3;
}

1897



Це не спрацьовує, якщо об'єкти мають однакові атрибути назви, і ви також хочете об'єднати атрибути. - Xiè Jìléi
Це робить лише невелику копію / злиття. Має потенціал, щоб приховати багато елементів. - Jay Taylor
+1, щоб визнати, що деякі бідні душі змушені використовувати рамки, які деруться по всьому їх прототипам ... - thejonwithnoh
Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1 це невірно з показаною реалізацією будівництва нового об'єкта. - scones
Це не допомогло мені через "Тому він присвоює властивості порівняно з просто копіюванням або визначенням нових властивостей, що може стати непридатним для об'єднання нових властивостей у прототип, якщо джерела злиття містять геттери". (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...) Мені довелося користуватися var merged = {...obj1, ...obj2}. - morgler


jQuery також має утиліту для цього: http://api.jquery.com/jQuery.extend/.

Зняті з документації jQuery:

// Merge options object into settings object
var settings = { validate: false, limit: 5, name: "foo" };
var options  = { validate: true, name: "bar" };
jQuery.extend(settings, options);

// Now the content of settings object is the following:
// { validate: true, limit: 5, name: "bar" }

Вищевказаний код буде змінювати існуючий об'єкт названий settings.


Якщо ви хочете створити a новий об'єкт не змінюючи жодного аргументу, використовуйте це:

var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };

/* Merge defaults and options, without modifying defaults */
var settings = $.extend({}, defaults, options);

// The content of settings variable is now the following:
// {validate: true, limit: 5, name: "bar"}
// The 'defaults' and 'options' variables remained the same.

1115



Гош, який названий погано. Дуже неправдоподібно знайти його при пошуку об'єднаних об'єктів. - Gregory
Увага: змінна "налаштування" буде змінена, хоча. jQuery не повертає новий екземпляр. Причиною цього (і для іменування) є те, що .extend () було розроблено для розширення об'єктів, а не для того, щоб збирати речі разом. Якщо ви хочете, щоб новий об'єкт (наприклад, параметри за замовчуванням не хотіли торкатися), ви завжди можете використовувати jQuery.extend ({}, параметри, параметри); - webmat
Зверніть увагу, jQuery.extend також має глибокий (логічний) параметр. jQuery.extend(true,settings,override), що важливо, якщо властивість в налаштуваннях містить об'єкт і перевизначає лише частину цього об'єкта. Замість того, щоб видалити неперевершені властивості, глибокий параметр буде оновлюватися тільки там, де він існує. За умовчанням помилково. - vol7ron
Аналогічним чином, якщо ви використовуєте underscore.js там underscorejs.org/#extend - twmulloy
Гей-Ауйкерс, вони дійсно зробили хорошу роботу, називаючи це. Якщо ви шукаєте, як розширити об'єкт Javascript, це станеться! Ви можете не потрапити на це, хоча, якщо ви намагаєтеся об'єднати об'єкти Javascript разом. - Derek Greer


The Гармонія ECMAScript 2015 (ES6) вказує Object.assign який буде робити це.

Object.assign(obj1, obj2);

Поточна підтримка браузера - це стає краще, але якщо ви розробляєте для браузерів, які не мають підтримки, ви можете використовувати поліфіль.


310



Зауважте, що це лише дрібне злиття - Joachim Lous
Я думаю тим часом Object.assign має досить пристойне покриття: kangax.github.io/compat-table/es6/... - LazerBass
Тепер це має бути правильною відповіддю. Люди в даний час складають свій код, щоб бути сумісний з рідкісним браузером (IE11), який не підтримує такого роду речі. Сторона примітка: якщо ви не хочете додавати obj2 до obj1, ви можете повернути новий об'єкт, використовуючи Object.assign({}, obj1, obj2) - Duvrai
@Duvrai Згідно з повідомленнями, які я бачив IE11, безумовно, не рідкість на рівні приблизно 18% ринку на кінець липня 2016 року. - NanoWizard
@Kermani ОП особливо відзначає, що рекурсія не потрібна. Тому, хоча корисно знати, що це рішення виконує межу злиття, ця відповідь є достатньою, як є. Коментар JoachimLou отримав добре заслужену версію і надає додаткову інформацію, коли це необхідно. Я думаю, що різниця між глибоким та неглибоким злиттям та подібними темами виходить за рамки цієї дискусії і краще підходить для інших питань СО. - NanoWizard


Я провів пошук за кодом, щоб об'єднати властивості об'єкта, і закінчився тут. Однак, оскільки не було коду для рекурсивного злиття, я сам це написав. (Може, jQuery розширює рекурсивний BTW?) Так чи інакше, сподіваюсь, хтось інший знайде це корисним також.

(Тепер код не використовується Object.prototype :)

Код

/*
* Recursively merge properties of two objects 
*/
function MergeRecursive(obj1, obj2) {

  for (var p in obj2) {
    try {
      // Property in destination object set; update its value.
      if ( obj2[p].constructor==Object ) {
        obj1[p] = MergeRecursive(obj1[p], obj2[p]);

      } else {
        obj1[p] = obj2[p];

      }

    } catch(e) {
      // Property in destination object not set; create it and set its value.
      obj1[p] = obj2[p];

    }
  }

  return obj1;
}

Приклад

o1 = {  a : 1,
        b : 2,
        c : {
          ca : 1,
          cb : 2,
          cc : {
            cca : 100,
            ccb : 200 } } };

o2 = {  a : 10,
        c : {
          ca : 10,
          cb : 20, 
          cc : {
            cca : 101,
            ccb : 202 } } };

o3 = MergeRecursive(o1, o2);

Виробляє об'єкт o3, як

o3 = {  a : 10,
        b : 2,
        c : {
          ca : 10,
          cb : 20,
          cc : { 
            cca : 101,
            ccb : 202 } } };

227



Красиво, але я спочатку зробив глибоку копію об'єктів. Таким чином, o1 також модифікується, оскільки об'єкти передаються за посиланням. - skerit
Це те, що я шукав. Переконайтеся, що перед тим, як перейти до сповіщення try attempt, перевірте, чи є obj.hasOwnProperty (p). В іншому випадку, ви, можливо, опиниться в іншому місці, коли об'єднувалися згори в ланцюжку прототипів. - Adam
Завдяки Маркусу. Зауважте, що o1 також змінюється MergeRecursive, тому наприкінці o1 та o3 ідентичні. Це може бути більш інтуїтивним, якщо ви нічого не повернете, тому користувач знає, що те, що вони постачають (o1), змінюється і стає результатом. Крім того, у вашому прикладі може бути варто додати щось у o2, яке не було спочатку в o1, щоб показати, що властивості o2 вставляються в o1 (хтось інший представлений нижче альтернативний код копіює ваш приклад, але їх розриви, коли o2 містить властивості не в o1 - але ваша робота в цьому відношенні). - pancake
@JonoRR - сам виклик робить його рекурсивним: obj1 [p] = MergeRecursive (obj1 [p], obj2 [p]); - z8000
Це хороша відповідь, але парочка вимовляє: 1) логіка не повинна грунтуватися на виключеннях; у цьому випадку будь-які винятки, які кинуті, будуть сприйняті непередбачуваними результатами. 2) Я погоджуюсь з @ pancake's comment про те, що нічого не повертає, оскільки оригінальний об'єкт змінений. Ось приклад jsFiddle без логіки виключення: jsfiddle.net/jlowery2663/z8at6knn/4 - Jeff Lowery - Jeff Lowery


Зауважте, що underscore.jsс extend-метод робить це в один вкладиш:

_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}

166



Цей приклад добре, оскільки ви маєте справу з анонімними об'єктами, але якщо це не так, то вебматеріалу коментар у відповідь jQuery Тут застосовується попередження про мутації, як і підкреслення мутації об'єкта призначення. Як і відповідь jQuery, щоб зробити це без мутацій, просто об'єднайте в порожній об'єкт: _({}).extend(obj1, obj2); - Abe Voelker
Є ще один варіант, який може бути важливим залежно від того, що ви намагаєтесь досягти: _.defaults(object, *defaults) "Заповніть нульові та невизначені властивості в об'єкті з значеннями з об'єктів за замовчуванням та поверніть об'єкт". - conny
Багато людей прокоментували мутацію об'єкта призначення, але замість того, щоб мати метод, створивши новий, загальний шаблон (наприклад, у додзе) - це dojo.mixin (dojo.clone (myobj), {newpropertys: "додати до міобь "}) - Colin D
Підкреслення може приймати довільну кількість об'єктів, тому ви можете уникнути мутації вихідного об'єкта, виконавши це var mergedObj = _.extend({}, obj1, obj2). - lobati


Подібно до jQuery extend (), у вас є однакові функції в AngularJS:

// Merge the 'options' object into the 'settings' object
var settings = {validate: false, limit: 5, name: "foo"};
var options  = {validate: true, name: "bar"};
angular.extend(settings, options);

80



ви також можете "запозичити" визначення extension з джерела JQ ... - juanmf
@ Юаньмф за що? - naXa
@naXa Я думаю, що це буде варіант, якщо ви не хочете додавати lib саме для цього fn. - juanmf


Мені потрібно об'єднати об'єкти сьогодні, і це питання (і відповіді) мені дуже допомогло. Я спробував деякі відповіді, але ніхто з них не відповідав моїм потребам, тому я поєднав деякі відповіді, додавав щось собі і придумав нову функцію об'єднання. Ось:

var merge = function() {
    var obj = {},
        i = 0,
        il = arguments.length,
        key;
    for (; i < il; i++) {
        for (key in arguments[i]) {
            if (arguments[i].hasOwnProperty(key)) {
                obj[key] = arguments[i][key];
            }
        }
    }
    return obj;
};

Деякі приклади використання:

var t1 = {
    key1: 1,
    key2: "test",
    key3: [5, 2, 76, 21]
};
var t2 = {
    key1: {
        ik1: "hello",
        ik2: "world",
        ik3: 3
    }
};
var t3 = {
    key2: 3,
    key3: {
        t1: 1,
        t2: 2,
        t3: {
            a1: 1,
            a2: 3,
            a4: [21, 3, 42, "asd"]
        }
    }
};

console.log(merge(t1, t2));
console.log(merge(t1, t3));
console.log(merge(t2, t3));
console.log(merge(t1, t2, t3));
console.log(merge({}, t1, { key1: 1 }));

59



Це чудова відповідь! Це просто як вихідний код jQuery! - Progo
Єдиним тегом на питання є javascript, тому це єдино правильна відповідь, а також велика. - skobaljic
Хороша відповідь, але я думаю, що якщо властивість присутня в першому і другому, і ви намагаєтеся створити новий об'єкт шляхом об'єднання першого та другого, то унікальні, присутні в першому об'єкті, будуть втрачені - Strikers
Але хіба це не очікувана поведінка? Мета цієї функції - переоцінити значення в порядку аргументів. Наступний буде перевизначати поточний. Як можна зберегти унікальні цінності? З мого прикладу, від чого ти очікуєш? merge({}, t1, { key1: 1 })? - Emre Erkan
Мої очікування полягатимуть у злитті лише типів, які не є об'єктами. - AGamePlayer


Ви можете використовувати властивості поширення об'єкта- поточна пропозиція ECMAScript 3-го етапу.

const obj1 = { food: 'pizza', car: 'ford' };
const obj2 = { animal: 'dog' };

const obj3 = { ...obj1, ...obj2 };
console.log(obj3);


43



Підтримка браузера? - Alph.Dev
@ Alph.Dev Ви знаєте, caniuse.com? - connexo
@connexo Для чого ви питаєте? Будь-ласка, надайте належну посилання на властивості розповсюдження об'єкта JS на сайті caniuse.com або не заважайте. Це не місце для гри смарт-осла. - Alph.Dev
Це конкретно не місце, де можна запитати речі, які всі можуть легко перевірити самостійно. - connexo
Я не можу отримати синтаксис 3 точки для роботи в node.js. вузол скаржиться на це. - blackhawk