วิธีการต่อคุณสมบัติจากวัตถุ JavaScript หลายตัว


106

ฉันกำลังมองหาวิธีที่ดีที่สุดในการ "เพิ่ม" ออบเจ็กต์ JavaScript หลายตัว (Associative Arrays)

ตัวอย่างเช่นกำหนด:

a = { "one" : 1, "two" : 2 };
b = { "three" : 3 };
c = { "four" : 4, "five" : 5 };

วิธีที่ดีที่สุดในการคำนวณคืออะไร:

{ "one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

3
หมายเหตุเชิงความหมาย: แม้จะมีไวยากรณ์ [] แต่ก็ไม่ใช่อาร์เรย์เลย ไม่รับประกันการสั่งซื้อจริงๆ
ÁlvaroGonzález

1
ไม่รับประกันลำดับการทำซ้ำตามมาตรฐาน ecma แต่เป็นวิธีการใช้งานในเบราว์เซอร์ส่วนใหญ่ (จาก John Resig) พฤติกรรมนี้ถูกทิ้งไว้อย่างชัดเจนโดยไม่ได้กำหนดโดยข้อกำหนด ECMAScript ใน ECMA-262 ส่วนที่ 12.6.4: กลศาสตร์ของการแจกแจงคุณสมบัติ ... ขึ้นอยู่กับการนำไปใช้งาน อย่างไรก็ตามข้อกำหนดค่อนข้างแตกต่างจากการใช้งาน การนำ ECMAScript ไปใช้งานสมัยใหม่ทั้งหมดจะวนซ้ำผ่านคุณสมบัติของอ็อบเจ็กต์ตามลำดับที่กำหนดไว้ ด้วยเหตุนี้ทีม Chrome จึงถือว่านี่เป็นข้อบกพร่องและจะดำเนินการแก้ไข
Juan Mendes

คำตอบ:


162

ECMAscript 6 ได้รับการแนะนำObject.assign()เพื่อให้บรรลุสิ่งนี้โดยกำเนิดใน Javascript

Object.assign ()วิธีการที่ใช้ในการคัดลอกค่าของคุณสมบัติของตัวเองทั้งหมดนับจากวัตถุหนึ่งหรือหลายแหล่งไปยังวัตถุเป้าหมาย มันจะส่งคืนวัตถุเป้าหมาย

เอกสาร MDN บน Object.assign ()

var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };

var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }

Object.assignได้รับการสนับสนุนในเบราว์เซอร์สมัยใหม่จำนวนมากแต่ยังไม่ครบทั้งหมด ใช้ตัวส่งสัญญาณเช่นBabelและTraceurเพื่อสร้าง ES5 JavaScript ที่เข้ากันได้แบบย้อนกลับ


4
นี่เป็นหนึ่งในสิ่งที่ดีที่สุดที่ฉันสามารถพูดได้ เนื่องจากตอนนี้ใช้ E6 เป็นส่วนใหญ่ Object.assign จึงเป็นคำตอบที่ดีที่สุด
Vimalraj Selvam

6
หากคุณไม่ทราบว่าต้องผสานวัตถุจำนวนเท่าใดเนื่องจากอยู่ในอาร์เรย์คุณสามารถรวมเข้าด้วยกันได้ดังนี้Object.assign.apply({}, [{a: 1}, {b: 2}, ....])
Jeanluca Scaljeri

1
อีกทางเลือกหนึ่งในการใช้Object.assign.applyเพื่อรวมอาร์เรย์ของวัตถุคือการใช้ตัวดำเนินการกระจายแทน:Object.assign( ...objects )
Spen

@Spen ที่รวมไว้เป็นคำตอบสำหรับคำถามนี้แล้ว ฉันไม่เห็นประโยชน์ของการทำซ้ำที่นี่
filoxo

หลังจาก 4 ชั่วโมงคำตอบนี้ช่วยแก้ปัญหาของฉันใน Angular 7 ขอบคุณสำหรับความเรียบง่ายและความถูกต้องของคำตอบ
Gel

35

คุณสามารถใช้ jquery $.extendเช่นนี้:

let a = { "one" : 1, "two" : 2 },
    b = { "three" : 3 },
    c = { "four" : 4, "five" : 5 };

let d = $.extend({}, a, b, c)

console.log(d)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


+1 แม้ว่าคุณจะไม่ได้ใช้วิธีของ jQuery แต่คุณสามารถใช้โค้ดของพวกเขาเพื่อช่วยสร้างการใช้งานของคุณเอง (อาจจะเจาะจงมากขึ้น)
tvanfosson

25
@Randal: มีเหตุผลดีๆมากมายที่ไม่ใช้ jQuery
Tim Down

3
แก้ไข:d = $.extend({},a,b,c);
Bob Stein

34

สิ่งนี้ควรทำ:

function collect() {
  var ret = {};
  var len = arguments.length;
  for (var i = 0; i < len; i++) {
    for (p in arguments[i]) {
      if (arguments[i].hasOwnProperty(p)) {
        ret[p] = arguments[i][p];
      }
    }
  }
  return ret;
}

let a = { "one" : 1, "two" : 2 };
let b = { "three" : 3 };
let c = { "four" : 4, "five" : 5 };

let d = collect(a, b, c);
console.log(d);

เอาท์พุต:

{
  "one": 1,
  "two": 2,
  "three": 3,
  "four": 4,
  "five": 5
}

ไม่lengthค้นหาขนาดของอาร์เรย์ในการเรียกแต่ละครั้งหรือไม่ ฉันเคยชินกับการเขียนfor (var i = 0, len = array.length; i < len; ++i)จนแทบจำไม่ได้ว่าทำไมฉันถึงเริ่มทำมัน
tvanfosson

1
ใช่ที่ถูกต้อง. ประสิทธิภาพที่ดีกว่าในการแคชความยาวครั้งเดียว อย่างไรก็ตามเนื่องจากขนาดของอาร์กิวเมนต์ "อาร์เรย์" ไม่น่าจะใหญ่มากนักจึงไม่สำคัญในกรณีนี้
jhurshman

1
ไม่ยกเว้นเล็กน้อยบน IE6 การเข้าถึงคุณสมบัติ length จะมีค่าใช้จ่ายเหมือนกับการเข้าถึงตัวแปร len
Alsciende

1
@Juan ฉันเชื่อว่าคุณพูดไม่ถูกและฉันได้ทำการทดสอบบางอย่างเพื่อตัดสินใจ การแคชความยาวเป็นตำนานง่ายๆของการเพิ่มประสิทธิภาพที่ล้าสมัยมาหลายปีแล้วและทำให้โค้ดอ่านได้น้อยลง (เล็กน้อย) จริงๆแล้วการแคชความยาวบางครั้งทำให้เบราว์เซอร์ช้าลง (Safari)
Alsciende

2
จะดีกว่าไหมถ้าเขียน "for (var p in ... " แทน "for (p in ... "
Hugo

24

ECMAScript 6 มีไวยากรณ์การแพร่กระจาย และตอนนี้คุณสามารถทำได้:

const obj1 = { 1: 11, 2: 22 };
const obj2 = { 3: 33, 4: 44 };
const obj3 = { ...obj1, ...obj2 };

console.log(obj3); // {1: 11, 2: 22, 3: 33, 4: 44}


12

ขีดล่างมีวิธีการไม่กี่วิธีในการทำเช่นนี้

1. _.extend (ปลายทาง * แหล่งที่มา)

คัดลอกคุณสมบัติทั้งหมดในออบเจ็กต์ต้นทางไปยังอ็อบเจ็กต์ปลายทางและส่งคืนอ็อบเจ็กต์ปลายทาง

_.extend(a, _.extend(b, c));
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

หรือ

_.extend(a, b);
=> {"one" : 1, "two" : 2, "three" : 3}
_.extend(a, c);
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

2. _.defaults (วัตถุ * ค่าเริ่มต้น)

กรอกข้อมูลในที่ไม่ได้กำหนดคุณสมบัติในวัตถุที่มีค่าจากค่าเริ่มต้นของวัตถุและกลับวัตถุ

_.defaults(a, _.defaults(b, c));
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

หรือ

_.defaults(a, b);
=> {"one" : 1, "two" : 2, "three" : 3}
_.defaults(a, c);
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

5

ตื้นโคลน (ไม่รวมต้นแบบ) หรือการควบรวมกิจการของวัตถุคือตอนนี้ไปได้โดยใช้ไวยากรณ์ที่สั้นกว่าObject.assign ()

ไวยากรณ์การแพร่กระจายสำหรับตัวอักษรอ็อบเจ็กต์ถูกนำมาใช้ใน ECMAScript 2018 ):

const a = { "one": 1, "two": 2 };
const b = { "three": 3 };
const c = { "four": 4, "five": 5 };

const result = {...a, ...b, ...c};
// Object { "one": 1, "two": 2 , "three": 3, "four": 4, "five": 5 }

ตัวดำเนินการ Spread (... )ได้รับการสนับสนุนในเบราว์เซอร์สมัยใหม่จำนวนมากแต่ไม่ใช่ทั้งหมด

ดังนั้นขอแนะนำให้ใช้Transpilerเช่นBabelเพื่อแปลงโค้ด ECMAScript 2015+ เป็น JavaScript เวอร์ชันที่เข้ากันได้แบบย้อนหลังในเบราว์เซอร์หรือสภาพแวดล้อมปัจจุบันและเก่ากว่า

นี่คือรหัสเทียบเท่าที่Babel จะสร้างให้คุณ:

"use strict";

var _extends = Object.assign || function(target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i];
    for (var key in source) {
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        target[key] = source[key];
      }
    }
  }
  return target;
};

var a = { "one": 1, "two": 2 };
var b = { "three": 3 };
var c = { "four": 4, "five": 5 };

var result = _extends({}, a, b, c);
// Object { "one": 1, "two": 2 , "three": 3, "four": 4, "five": 5 }

นี่เป็นคำตอบที่มีประสิทธิภาพมากกว่าคำตอบที่ยอมรับ ขอบคุณ!
Kaleb Anderson

4

เหตุใดจึงควร จำกัด ฟังก์ชันไว้ที่ 3 อาร์กิวเมนต์ ตรวจสอบhasOwnPropertyด้วย

function Collect() {
    var o={};
    for(var i=0;i<arguments.length;i++) {
      var arg=arguments[i];
      if(typeof arg != "object") continue;
      for(var p in arg) {
        if(arg.hasOwnProperty(p)) o[p] = arg[p];
      }
    }
    return o;
}

3
function Collect(a, b, c) {
    for (property in b)
        a[property] = b[property];

    for (property in c)
        a[property] = c[property];

    return a;
}

ข้อสังเกต: คุณสมบัติที่มีอยู่ในวัตถุก่อนหน้าจะถูกเขียนทับ


2
สิ่งนี้มีผลข้างเคียงที่a === dในตอนท้าย นั่นอาจจะโอเคและมันอาจจะไม่
tvanfosson

3

เป็นเรื่องง่ายโดยใช้ตัวดำเนินการแพร่กระจาย ES7สำหรับวัตถุในคอนโซลเบราว์เซอร์ของคุณ

({ name: "Alex", ...(true  ? { age: 19 } : { })}) // {name: "Alex", age: 19}
({ name: "Alex", ...(false ? { age: 19 } : { })}) // {name: "Alex",        }

1
ดี. คำถามนี้มีอายุมากกว่า 10 ปีดีใจที่ตอนนี้ใช้งานง่ายเช่นนี้ได้ง่าย มันไม่ง่ายอย่างนั้นมาก่อนหากคุณดูคำตอบของคนอื่น
Vlad

นั่นคือเหตุผลที่ฉันทิ้งคำตอบของตัวเอง :)
Purkhalo Alex

2

การผสานจำนวนแบบไดนามิกของวัตถุที่เราสามารถใช้Object.assignกับการแพร่กระจายไวยากรณ์

const mergeObjs = (...objs) => Object.assign({}, ...objs);

ฟังก์ชั่นข้างต้นยอมรับวัตถุจำนวนเท่าใดก็ได้รวมคุณสมบัติทั้งหมดเข้ากับวัตถุใหม่โดยมีคุณสมบัติจากวัตถุในภายหลังเขียนทับสิ่งเหล่านั้นจากวัตถุก่อนหน้า

การสาธิต:

ในการผสานอาร์เรย์ของวัตถุอาจใช้วิธีการที่คล้ายกัน

const mergeArrayOfObjs = arr => Object.assign({}, ...arr);

การสาธิต:


1

เป็นไปได้ว่าวิธีที่เร็วที่สุดมีประสิทธิภาพและทั่วไปกว่าคือวิธีนี้ (คุณสามารถรวมวัตถุจำนวนเท่าใดก็ได้และแม้แต่คัดลอกไปยังรายการแรก -> กำหนด):

function object_merge(){
    for (var i=1; i<arguments.length; i++)
       for (var a in arguments[i])
         arguments[0][a] = arguments[i][a];
   return arguments[0];
}

นอกจากนี้ยังช่วยให้คุณแก้ไขวัตถุแรกเมื่อส่งผ่านโดยการอ้างอิง หากคุณไม่ต้องการสิ่งนี้ แต่ต้องการมีวัตถุใหม่ที่มีคุณสมบัติทั้งหมดคุณสามารถส่ง {} เป็นอาร์กิวเมนต์แรกได้

var object1={a:1,b:2};
var object2={c:3,d:4};
var object3={d:5,e:6};
var combined_object=object_merge(object1,object2,object3); 

Combined_object และ object1 ทั้งสองมีคุณสมบัติของ object1, object2, object3

var object1={a:1,b:2};
var object2={c:3,d:4};
var object3={d:5,e:6};
var combined_object=object_merge({},object1,object2,object3); 

ในกรณีนี้ Combined_object มีคุณสมบัติของ object1, object2, object3 แต่ object1 ไม่ได้ถูกแก้ไข

ตรวจสอบที่นี่: https://jsfiddle.net/ppwovxey/1/

หมายเหตุ: วัตถุ JavaScript ถูกส่งผ่านโดยการอ้างอิง


1

ES6 ++

คำถามคือการเพิ่มต่าง ๆที่แตกต่างกันวัตถุที่เป็นหนึ่ง

let obj = {};
const obj1 = { foo: 'bar' };
const obj2 = { bar: 'foo' };
Object.assign(obj, obj1, obj2);
//output => {foo: 'bar', bar: 'foo'};

สมมติว่าคุณมีวัตถุชิ้นเดียวที่มีหลายปุ่มซึ่งเป็นวัตถุ:

let obj = {
  foo: { bar: 'foo' },
  bar: { foo: 'bar' }
}

นี่คือทางออกที่ฉันพบ (ยังต้อง foreach: /)

let objAll = {};

Object.values(obj).forEach(o => {
  objAll = {...objAll, ...o};
});

การทำเช่นนี้ทำให้เราสามารถเพิ่มคีย์อ็อบเจ็กต์ทั้งหมดลงในปุ่มเดียวได้

// Output => { bar: 'foo', foo: 'bar' }


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.