Câu hỏi Làm thế nào tôi có thể hợp nhất các thuộc tính của hai đối tượng JavaScript động?


Tôi cần có khả năng hợp nhất hai đối tượng JavaScript (rất đơn giản) trong thời gian chạy. Ví dụ tôi muốn:

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

obj1.merge(obj2);

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

Có ai có một kịch bản cho điều này hoặc biết một cách xây dựng trong cách để làm điều này? Tôi không cần đệ quy, và tôi không cần phải kết hợp các hàm, chỉ các phương thức trên các đối tượng phẳng.


1831


gốc


Object.assign .... - caub


Các câu trả lời:


Phương pháp chuẩn ECMAScript 2018

Bạn sẽ sử dụng lây lan đối tượng:

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};

Phương pháp chuẩn 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);

(xem Tham chiếu JavaScript MDN)


Phương pháp cho ES5 và trước đó

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

Lưu ý rằng điều này sẽ chỉ thêm tất cả các thuộc tính của obj2 đến obj1 có thể không phải là thứ bạn muốn nếu bạn vẫn muốn sử dụng chưa được sửa đổi obj1.

Nếu bạn đang sử dụng một khuôn khổ mà craps trên tất cả các nguyên mẫu của bạn sau đó bạn có để có được fancier với các kiểm tra như hasOwnProperty, nhưng mã đó sẽ làm việc cho 99% các trường hợp.

Hàm ví dụ:

/**
 * 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;
}

1898



Điều này không hoạt động nếu các đối tượng có cùng thuộc tính tên và bạn cũng muốn hợp nhất các thuộc tính. - Xiè Jìléi
Điều này chỉ làm một bản sao / hợp nhất nông. Có tiềm năng để clobber rất nhiều yếu tố. - Jay Taylor
1 vì đã thừa nhận rằng một số linh hồn tội nghiệp bị buộc phải sử dụng các khuôn khổ crap trên tất cả các nguyên mẫu của họ ... - thejonwithnoh
Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1 điều này là không đúng với việc thực hiện được hiển thị xây dựng một đối tượng mới. - scones
Điều này đã không làm việc cho tôi vì "Vì vậy nó gán tài sản so với chỉ sao chép hoặc xác định tài sản mới. Điều này có thể làm cho nó không phù hợp cho việc sáp nhập tài sản mới vào một nguyên mẫu nếu các nguồn hợp nhất chứa getters." (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…). Tôi đã phải sử dụng var merged = {...obj1, ...obj2}. - morgler


jQuery cũng có một tiện ích cho việc này: http://api.jquery.com/jQuery.extend/.

Lấy từ tài liệu 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" }

Đoạn mã trên sẽ biến đổi đối tượng hiện có có tên settings.


Nếu bạn muốn tạo đối tượng mới mà không sửa đổi một trong hai đối số, hãy sử dụng điều này:

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



Gosh được đặt tên kém. Rất khó tìm thấy nó khi tìm kiếm cách hợp nhất các đối tượng. - Gregory
Cẩn thận: biến "cài đặt" sẽ được sửa đổi. jQuery không trả về một cá thể mới. Lý do cho việc này (và để đặt tên) là .extend () được phát triển để mở rộng các đối tượng, chứ không phải để trộn lẫn các công cụ với nhau. Nếu bạn muốn một đối tượng mới (ví dụ: cài đặt mặc định bạn không muốn chạm), bạn luôn có thể jQuery.extend ({}, cài đặt, tùy chọn); - webmat
Tâm trí bạn, jQuery.extend cũng có một thiết lập sâu (boolean). jQuery.extend(true,settings,override), điều quan trọng nếu một thuộc tính trong cài đặt chứa đối tượng và ghi đè chỉ có một phần của đối tượng đó. Thay vì xóa các thuộc tính chưa được so khớp, cài đặt sâu sẽ chỉ cập nhật vị trí của nó. Mặc định này sai. - vol7ron
tương tự, nếu bạn đang sử dụng underscore.js ở đó underscorejs.org/#extend - twmulloy
Gee willikers, họ thực sự đã làm một công việc tốt đặt tên này. Nếu bạn tìm kiếm cách mở rộng đối tượng Javascript, nó sẽ xuất hiện ngay! Bạn có thể không nhấn vào nó mặc dù nếu bạn đang cố gắng để kết hợp hoặc khâu các đối tượng Javascript với nhau. - Derek Greer


Các Harmony ECMAScript 2015 (ES6) chỉ định Object.assign sẽ làm điều này.

Object.assign(obj1, obj2);

Hỗ trợ trình duyệt hiện tại là trở nên tốt hơn, nhưng nếu bạn đang phát triển cho các trình duyệt không có hỗ trợ, bạn có thể sử dụng polyfill.


310



Lưu ý rằng đây chỉ là hợp nhất nông - Joachim Lous
Tôi nghĩ trong khi đó Object.assign có mức độ phù hợp khá tốt: kangax.github.io/compat-table/es6/… - LazerBass
Điều này bây giờ sẽ là câu trả lời đúng. Mọi người ngày nay biên dịch mã của họ để tương thích với trình duyệt hiếm (IE11) không hỗ trợ loại điều này. Lưu ý phụ: nếu bạn không muốn thêm obj2 vào obj1, bạn có thể trả về một đối tượng mới bằng cách sử dụng Object.assign({}, obj1, obj2) - Duvrai
@ Duvrai Theo các báo cáo tôi đã thấy, IE11 chắc chắn không phải là hiếm vào khoảng 18% thị phần tính đến tháng 7 năm 2016. - NanoWizard
@Kermani OP đặc biệt chỉ ra rằng đệ quy là không cần thiết. Vì vậy, trong khi nó rất hữu ích để biết rằng giải pháp này thực hiện một hợp nhất nông, câu trả lời này là đủ như. Lời bình luận của JoachimLou đã nhận được những lời khen ngợi xứng đáng và cung cấp thêm thông tin khi cần thiết. Tôi nghĩ sự khác biệt giữa việc sáp nhập sâu và nông và các chủ đề tương tự nằm ngoài phạm vi của cuộc thảo luận này và phù hợp hơn với các câu hỏi SO khác. - NanoWizard


Tôi googled cho mã để hợp nhất các thuộc tính đối tượng và kết thúc ở đây. Tuy nhiên vì không có bất kỳ mã nào để hợp nhất đệ quy, tôi đã tự viết nó. (Có lẽ jQuery mở rộng là đệ quy BTW?) Dù sao, hy vọng một người nào khác sẽ tìm thấy nó hữu ích là tốt.

(Bây giờ mã không sử dụng Object.prototype :)

Mã số

/*
* 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;
}

Một ví dụ

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);

Tạo đối tượng o3 như

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

227



Thật tuyệt, nhưng trước hết tôi sẽ làm một bản đồ sâu sắc của các vật thể. Bằng cách này, o1 cũng sẽ được sửa đổi, vì các đối tượng được truyền qua tham chiếu. - skerit
Đây là những gì tôi đang tìm kiếm. Hãy chắc chắn kiểm tra xem obj2.hasOwnProperty (p) trước khi bạn đi đến câu lệnh try catch. Nếu không, bạn có thể kết thúc với một số crap khác nhận được sáp nhập từ cao hơn trong chuỗi nguyên mẫu. - Adam
Cảm ơn Markus. Lưu ý rằng o1 cũng được sửa đổi bởi MergeRecursive, do đó, ở cuối, o1 và o3 là ​​giống hệt nhau. Nó có thể trực quan hơn nếu bạn không trả lại bất cứ điều gì, vì vậy người dùng biết rằng những gì họ đang cung cấp (o1) được sửa đổi và trở thành kết quả. Ngoài ra, trong ví dụ của bạn, nó có thể có giá trị thêm một cái gì đó vào o2 mà không phải là ban đầu trong o1, để cho thấy rằng o2 tài sản được chèn vào o1 (người khác dưới đây gửi mã thay thế sao chép ví dụ của bạn, nhưng họ phá vỡ khi o2 chứa tài sản không trong o1 - nhưng bạn hoạt động trong lĩnh vực này). - pancake
@ JonoRR - cuộc gọi đến chính nó làm cho nó đệ quy: obj1 [p] = MergeRecursive (obj1 [p], obj2 [p]); - z8000
Đây là một câu trả lời hay, nhưng một vài câu hỏi: 1) Không có logic nào được dựa trên ngoại lệ; trong trường hợp này, bất kỳ ngoại lệ nào được ném sẽ bị bắt với kết quả không thể đoán trước. 2) Tôi đồng ý với nhận xét của @ pancake về việc không trả lại bất cứ điều gì, vì đối tượng gốc được sửa đổi. Đây là một ví dụ jsFiddle không có logic ngoại lệ: jsfiddle.net/jlowery2663/z8at6knn/4 - Jeff Lowery - Jeff Lowery


Lưu ý rằng underscore.js'S extend-phương pháp làm điều này trong một lớp lót:

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

166



Ví dụ này là tốt như bạn đang đối phó với các đối tượng vô danh, nhưng nếu đây không phải là trường hợp thì webmat bình luận trong câu trả lời jQuery cảnh báo về các đột biến áp dụng ở đây, dưới dạng dấu gạch dưới cũng thay đổi đối tượng đích. Giống như câu trả lời jQuery, để làm điều này đột biến-miễn phí chỉ cần nhập vào một đối tượng trống: _({}).extend(obj1, obj2); - Abe Voelker
Có một số khác có thể phù hợp tùy thuộc vào những gì bạn đang cố gắng đạt được: _.defaults(object, *defaults) "Điền vào các thuộc tính null và undefined trong đối tượng với các giá trị từ các đối tượng mặc định, và trả về đối tượng." - conny
Nhiều người đã nhận xét về việc biến đổi đối tượng đích, nhưng thay vì có một phương thức tạo một đối tượng mới, một mẫu chung (ví dụ trong dojo) là nói dojo.mixin (dojo.clone (myobj), {newpropertys: "để thêm tới myobj "}) - Colin D
Dấu gạch dưới có thể lấy số lượng đối tượng tùy ý, vì vậy bạn có thể tránh đột biến đối tượng gốc bằng cách thực hiện var mergedObj = _.extend({}, obj1, obj2). - lobati


Tương tự như jQuery extend (), bạn có cùng chức năng trong 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);

81



bạn cũng có thể "mượn" định nghĩa mở rộng từ nguồn JQ ... - juanmf
@ juanmf làm gì? - naXa
@naXa Tôi nghĩ rằng nó sẽ là một lựa chọn nếu bạn không muốn thêm một lib chỉ cho fn này. - juanmf


Tôi cần phải kết hợp các đối tượng ngày hôm nay, và câu hỏi này (và câu trả lời) đã giúp tôi rất nhiều. Tôi đã thử một số câu trả lời, nhưng không ai trong số họ phù hợp với nhu cầu của tôi, vì vậy tôi kết hợp một số câu trả lời, thêm một cái gì đó bản thân mình và đã đưa ra một chức năng hợp nhất mới. Đây là:

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;
};

Một số ví dụ sử dụng:

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



Đây là một câu trả lời tuyệt vời! Nó giống như mã nguồn jQuery! - Progo
Thẻ duy nhất cho câu hỏi là javascript, vì vậy đây là câu trả lời đúng và cũng là câu trả lời tuyệt vời nhất. - skobaljic
câu trả lời hay nhưng tôi nghĩ nếu một tài sản có mặt ở cái đầu tiên và thứ hai và bạn đang cố gắng tạo một đối tượng mới bằng cách hợp nhất đầu tiên và thứ hai thì những cái duy nhất có trong đối tượng đầu tiên sẽ bị mất - Strikers
Nhưng nó không phải là hành vi mong đợi? Mục đích của hàm này là ghi đè các giá trị trong thứ tự đối số. Tiếp theo sẽ ghi đè lên hiện tại. Làm thế nào bạn có thể duy trì các giá trị duy nhất? Từ ví dụ của tôi, bạn mong đợi điều gì từ merge({}, t1, { key1: 1 })? - Emre Erkan
Kỳ vọng của tôi là chỉ hợp nhất các giá trị không thuộc đối tượng. - AGamePlayer