Lodash - ความแตกต่างระหว่าง. ขยาย () /. มอบหมาย () และ. ผสาน ()


คำตอบ:


583

นี่คือวิธีextend/ การassignทำงาน: สำหรับแต่ละคุณสมบัติในแหล่งที่มาให้คัดลอกค่าตามที่เป็นไปยังปลายทาง หากค่าคุณสมบัติเป็นวัตถุเองก็จะไม่มีคุณสมบัติการแวะเวียนแบบเรียกซ้ำ วัตถุทั้งหมดจะถูกนำมาจากแหล่งที่มาและกำหนดไว้ในปลายทาง

นี่เป็นวิธีการmergeทำงาน: สำหรับแต่ละคุณสมบัติในแหล่งข้อมูลให้ตรวจสอบว่าคุณสมบัตินั้นเป็นวัตถุเอง ถ้าเป็นไปแล้วซ้ำและพยายามแมปคุณสมบัติวัตถุลูกจากต้นทางไปยังปลายทาง ดังนั้นเราจึงรวมลำดับชั้นของวัตถุจากต้นทางไปยังปลายทาง ในขณะที่สำหรับextend/ assignมันเป็นสำเนาของคุณสมบัติในระดับง่าย ๆ จากต้นทางไปยังปลายทาง

นี่คือ JSBin ง่าย ๆ ที่จะทำให้คริสตัลนี้ใส: http://jsbin.com/uXaqIMa/2/edit?js,console

นี่คือเวอร์ชั่นที่ซับซ้อนยิ่งขึ้นซึ่งรวมถึงอาเรย์ในตัวอย่างด้วย: http://jsbin.com/uXaqIMa/1/edit?js,console


16
ความแตกต่างที่สำคัญน่าจะเป็นในขณะที่ _.merge ส่งคืนวัตถุที่ถูกผสานใหม่ _.extend จะทำการเปลี่ยนแปลงวัตถุปลายทางแบบแทนที่
letronje

69
พวกเขาทั้งคู่ดูเหมือนจะกลายพันธุ์วัตถุปลายทางโดยไม่คำนึงถึงสิ่งที่พวกเขากลับมา
เจสันไรซ์

7
นอกจากนี้ยังปรากฏว่า _.extend ปิดกั้นสมาชิกของวัตถุปลายทางหากพวกเขาไม่ได้อยู่ในวัตถุต้นทางซึ่งน่าแปลกใจสำหรับฉัน
เจสันข้าว

5
@ JasonRice พวกเขาไม่ได้รับการอุดตัน ยกตัวอย่างเช่นในซอนี้ "เป็น" สถานที่ให้บริการไม่ได้รับ clobbered มันเป็นความจริงที่ว่าหลังจากที่ขยายแล้วปลายทาง ["p"] ["y"] ไม่มีอยู่อีกต่อไป - นี่เป็นเพราะก่อนที่จะขยาย src และปลายทางทั้งคู่มีคุณสมบัติ "p" ดังนั้นคุณสมบัติ "p" ของ dest จึงถูกเขียนทับอย่างสมบูรณ์ โดยคุณสมบัติ "p" ของ src (พวกเขาเป็นวัตถุเดียวกันที่แน่นอนในขณะนี้)
Kevin Wheeler

14
เพื่อให้ชัดเจนทั้งสองวิธีจะแก้ไข / เขียนทับอาร์กิวเมนต์แรกโดยอ้างอิง ดังนั้นหากคุณต้องการวัตถุใหม่จากการผสานที่เกิดขึ้นควรผ่านวัตถุที่แท้จริง var combined = merge({}, src, dest)
Jon Jaques

534

รุ่น Lodash 3.10.1

วิธีเปรียบเทียบ

  • _.merge(object, [sources], [customizer], [thisArg])
  • _.assign(object, [sources], [customizer], [thisArg])
  • _.extend(object, [sources], [customizer], [thisArg])
  • _.defaults(object, [sources])
  • _.defaultsDeep(object, [sources])

ความคล้ายคลึงกัน

  • ไม่มีใครทำงานในอาร์เรย์ได้ตามที่คุณคาดหวัง
  • _.extendเป็นชื่อแทน_.assignดังนั้นจึงเหมือนกัน
  • ดูเหมือนว่าทั้งหมดจะปรับเปลี่ยนวัตถุเป้าหมาย (อาร์กิวเมนต์แรก)
  • พวกเขาทั้งหมดจัดการnullเดียวกัน

ความแตกต่าง

  • _.defaultsและ_.defaultsDeepประมวลผลข้อโต้แย้งในลำดับย้อนกลับเมื่อเปรียบเทียบกับข้ออื่น (แม้ว่าอาร์กิวเมนต์แรกยังเป็นวัตถุเป้าหมาย)
  • _.mergeและ_.defaultsDeepจะรวมวัตถุลูกและวัตถุอื่น ๆ จะเขียนทับในระดับราก
  • เท่านั้น_.assignและ_.extendจะเขียนทับค่าด้วยundefined

การทดสอบ

พวกเขาทั้งหมดจัดการสมาชิกที่รูทในลักษณะที่คล้ายกัน

_.assign      ({}, { a: 'a' }, { a: 'bb' }) // => { a: "bb" }
_.merge       ({}, { a: 'a' }, { a: 'bb' }) // => { a: "bb" }
_.defaults    ({}, { a: 'a' }, { a: 'bb' }) // => { a: "a"  }
_.defaultsDeep({}, { a: 'a' }, { a: 'bb' }) // => { a: "a"  }

_.assignจับundefinedแต่คนอื่น ๆ จะข้ามมัน

_.assign      ({}, { a: 'a'  }, { a: undefined }) // => { a: undefined }
_.merge       ({}, { a: 'a'  }, { a: undefined }) // => { a: "a" }
_.defaults    ({}, { a: undefined }, { a: 'bb' }) // => { a: "bb" }
_.defaultsDeep({}, { a: undefined }, { a: 'bb' }) // => { a: "bb" }

พวกเขาทั้งหมดจัดการnullเดียวกัน

_.assign      ({}, { a: 'a'  }, { a: null }) // => { a: null }
_.merge       ({}, { a: 'a'  }, { a: null }) // => { a: null }
_.defaults    ({}, { a: null }, { a: 'bb' }) // => { a: null }
_.defaultsDeep({}, { a: null }, { a: 'bb' }) // => { a: null }

แต่เพียง_.mergeและ_.defaultsDeepจะรวมวัตถุลูก

_.assign      ({}, {a:{a:'a'}}, {a:{b:'bb'}}) // => { "a": { "b": "bb" }}
_.merge       ({}, {a:{a:'a'}}, {a:{b:'bb'}}) // => { "a": { "a": "a", "b": "bb" }}
_.defaults    ({}, {a:{a:'a'}}, {a:{b:'bb'}}) // => { "a": { "a": "a" }}
_.defaultsDeep({}, {a:{a:'a'}}, {a:{b:'bb'}}) // => { "a": { "a": "a", "b": "bb" }}

และไม่มีใครในพวกเขาจะรวมอาร์เรย์ดูเหมือนว่า

_.assign      ({}, {a:['a']}, {a:['bb']}) // => { "a": [ "bb" ] }
_.merge       ({}, {a:['a']}, {a:['bb']}) // => { "a": [ "bb" ] }
_.defaults    ({}, {a:['a']}, {a:['bb']}) // => { "a": [ "a"  ] }
_.defaultsDeep({}, {a:['a']}, {a:['bb']}) // => { "a": [ "a"  ] }

ทั้งหมดปรับเปลี่ยนวัตถุเป้าหมาย

a={a:'a'}; _.assign      (a, {b:'bb'}); // a => { a: "a", b: "bb" }
a={a:'a'}; _.merge       (a, {b:'bb'}); // a => { a: "a", b: "bb" }
a={a:'a'}; _.defaults    (a, {b:'bb'}); // a => { a: "a", b: "bb" }
a={a:'a'}; _.defaultsDeep(a, {b:'bb'}); // a => { a: "a", b: "bb" }

ไม่มีใครทำงานได้ตามที่คาดหวังในอาร์เรย์

หมายเหตุ: ตามที่ @Mistic ชี้ให้เห็นว่า Lodash ใช้อาร์เรย์เป็นวัตถุที่มีคีย์เป็นดัชนีในอาร์เรย์

_.assign      ([], ['a'], ['bb']) // => [ "bb" ]
_.merge       ([], ['a'], ['bb']) // => [ "bb" ]
_.defaults    ([], ['a'], ['bb']) // => [ "a"  ]
_.defaultsDeep([], ['a'], ['bb']) // => [ "a"  ]

_.assign      ([], ['a','b'], ['bb']) // => [ "bb", "b" ]
_.merge       ([], ['a','b'], ['bb']) // => [ "bb", "b" ]
_.defaults    ([], ['a','b'], ['bb']) // => [ "a", "b"  ]
_.defaultsDeep([], ['a','b'], ['bb']) // => [ "a", "b"  ]

32
จริง ๆ แล้วมันรวมอาร์เรย์เหมือนกับที่ผสานวัตถุเพราะอาร์เรย์เป็นวัตถุที่มีคีย์ตัวเลข ฉันยอมรับว่าจะมีใครคาดว่าจะต่อกันหรือแทนที่อาร์เรย์ขึ้นอยู่กับการใช้งาน
Mistic

11
คำตอบที่ยอดเยี่ยม การทดสอบใช้การสอนอย่างมาก :-)
Lucio Paiva

5
_.extend is an alias for _.assign, so they are identicalขัดแย้งกับOnly _.assign will overwrite a value with undefined
Chazt3n

9
ในฐานะของ v4.0 ปัจจุบัน _.extend เป็นชื่อแทนของ _.assignIn ไม่ใช่ _assign ฟังก์ชัน assignIn เพิ่มการจัดการกับคุณสมบัติที่สืบทอด
Mike Hedman

2
จะถือว่าเป็นโมฆะเช่นเดียวกับ undifined ที่นี่?
C_B

75

ข้อแตกต่างอีกประการที่ควรคำนึงถึงคือการจัดการundefinedค่า:

mergeInto = { a: 1}
toMerge = {a : undefined, b:undefined}
lodash.extend({}, mergeInto, toMerge) // => {a: undefined, b:undefined}
lodash.merge({}, mergeInto, toMerge)  // => {a: 1, b:undefined}

ดังนั้นmergeจะไม่รวมundefinedค่าเป็นค่าที่กำหนด


3
มันเป็นเพียงฉันหรือไม่ที่จะทำให้ lodash.extend ไร้ประโยชน์อย่างสมบูรณ์ในการที่จะส่งกลับโคลนของวัตถุ 'toMerge' เสมอ?
เจสันไรซ์

6
หากmergeIntoมีคุณสมบัติที่toMergeไม่ได้มีก็จะรักษาคุณสมบัติเหล่านั้น ในกรณีนี้มันจะไม่ลอกแบบ
David Neale

1
@JasonRice ลบว่าง {} และมันจะรวมอยู่ในสถานที่ lodash.merge (mergeInto, toMerge)
sidonaldson

20

การพิจารณาสิ่งที่พวกเขาทำจากมุมมองทางความหมายอาจเป็นประโยชน์เช่นกัน

_.กำหนด

   will assign the values of the properties of its second parameter and so on,
   as properties with the same name of the first parameter. (shallow copy & override)

_.merge

   merge is like assign but does not assign objects but replicates them instead.
  (deep copy)

_.defaults

   provides default values for missing values.
   so will assign only values for keys that do not exist yet in the source.

_.defaultsDeep

   works like _defaults but like merge will not simply copy objects
   and will use recursion instead.

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


3

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

obj = _.assign(obj, _.merge(obj, [source]))

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