Lodash - sự khác biệt giữa .extend () / .assign () và .merge ()


Câu trả lời:


583

Dưới đây là cách extend/ assignhoạt động: Đối với mỗi thuộc tính trong nguồn, hãy sao chép giá trị của nó như là đích đến. nếu bản thân các giá trị thuộc tính là các đối tượng, thì không có giao thức đệ quy của các thuộc tính của chúng. Toàn bộ đối tượng sẽ được lấy từ nguồn và đặt vào đích.

Đây là cách mergehoạt động: Đối với mỗi thuộc tính trong nguồn, hãy kiểm tra xem thuộc tính đó có phải là đối tượng không. Nếu đó là đi xuống đệ quy và cố gắng ánh xạ các thuộc tính đối tượng con từ nguồn đến đích. Vì vậy, về cơ bản chúng tôi hợp nhất hệ thống phân cấp đối tượng từ nguồn đến đích. Trong khi cho extend/ assign, nó đơn giản là một bản sao thuộc tính từ nguồn đến đích.

Đây là một JSBin đơn giản sẽ làm cho tinh thể này rõ ràng: http://jsbin.com/uXaqIMa/2/edit?js,console

Đây là phiên bản phức tạp hơn bao gồm cả mảng trong ví dụ: http://jsbin.com/uXaqIMa/1/edit?js,console


16
Một sự khác biệt quan trọng dường như là trong khi _.merge trả về một đối tượng được hợp nhất mới, _.extend sẽ biến đổi đối tượng đích tại chỗ,
letronje

69
Cả hai đều xuất hiện để đột biến đối tượng đích bất kể họ trả lại cái gì.
Jason Rice

7
Nó cũng xuất hiện rằng _.extend chặn các thành viên của đối tượng đích nếu chúng không có trong đối tượng nguồn, điều này gây bất ngờ cho tôi.
Jason Rice

5
@JasonRice Họ không bị tắc nghẽn. Ví dụ, trong câu đố này, thuộc tính "a" không bị ghi đè . Đúng là sau khi gia hạn, mệnh ["p"] ["y"] không còn tồn tại - Điều này là do trước khi mở rộng src và mệnh đều có thuộc tính "p", do đó, thuộc tính "p" của mệnh bị ghi đè hoàn toàn bởi thuộc tính "p" của src (bây giờ chúng là cùng một đối tượng).
Kevin Wheeler

14
Để rõ ràng, cả hai phương pháp đều sửa đổi / ghi đè đối số đầu tiên bằng tham chiếu. Vì vậy, nếu bạn muốn một đối tượng mới từ hợp nhất kết quả, tốt nhất là vượt qua một đối tượng theo nghĩa đen. var combined = merge({}, src, dest)
Jon Jaques

534

Phiên bản tạm thời 3.10.1

Phương pháp so sánh

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

Điểm tương đồng

  • Không ai trong số họ làm việc trên mảng như bạn mong đợi
  • _.extendlà một bí danh cho _.assign, vì vậy chúng giống hệt nhau
  • Tất cả chúng dường như sửa đổi đối tượng đích (đối số đầu tiên)
  • Tất cả đều xử lý nullnhư nhau

Sự khác biệt

  • _.defaults_.defaultsDeepxử lý các đối số theo thứ tự ngược lại so với các đối số khác (mặc dù đối số đầu tiên vẫn là đối tượng đích)
  • _.merge_.defaultsDeepsẽ hợp nhất các đối tượng con và các đối tượng khác sẽ ghi đè lên ở cấp gốc
  • Chỉ _.assign_.extendsẽ ghi đè lên một giá trị vớiundefined

Xét nghiệm

Họ đều xử lý các thành viên ở gốc theo những cách tương tự.

_.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"  }

_.assignxử lý undefinednhưng những người khác sẽ bỏ qua nó

_.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" }

Tất cả đều xử lý nullnhư nhau

_.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 }

Nhưng chỉ _.merge_.defaultsDeepsẽ hợp nhất các đối tượng con

_.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" }}

Và không ai trong số họ sẽ hợp nhất các mảng có vẻ như

_.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"  ] }

Tất cả sửa đổi đối tượng đích

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

Không ai thực sự làm việc như mong đợi trên mảng

Lưu ý: Như @Mistic đã chỉ ra, Lodash coi các mảng là các đối tượng trong đó các khóa là chỉ mục vào mảng.

_.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
Nó thực sự hợp nhất các mảng chính xác như nó hợp nhất các đối tượng, bởi vì các mảng là các đối tượng với các khóa số. Tôi đồng ý rằng người ta sẽ mong muốn ghép nối hoặc thay thế các mảng, tùy thuộc vào cách sử dụng.
Mistic

11
Câu trả lời tuyệt vời. Các bài kiểm tra rất mô phạm :-)
Lucio Paiva

5
_.extend is an alias for _.assign, so they are identicalxung đột vớiOnly _.assign will overwrite a value with undefined
Chazt3n

9
Kể từ phiên bản 4.0, _.extend hiện là bí danh cho _.assignIn, không phải _assign. Hàm gánIn thêm xử lý các thuộc tính được kế thừa.
Mike Hedman

2
null được đối xử giống như không xác định ở đây?
C_B

75

Một điểm khác biệt cần chú ý là xử lý các undefinedgiá trị:

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

Vì vậy, mergesẽ không hợp nhất undefinedcác giá trị thành các giá trị được xác định.


3
Có phải chỉ tôi hay nó làm cho lodash.extend hoàn toàn vô dụng ở chỗ nó luôn trả về một bản sao của đối tượng 'toMerge'?
Jason Rice

6
Nếu mergeIntocó các thuộc tính toMergekhông có thì nó sẽ giữ lại các thuộc tính đó. Trong trường hợp đó, nó sẽ không phải là một bản sao.
David Neale

1
@JasonRice xóa {} trống và nó sẽ hợp nhất nó vào vị trí lodash.merge (mergeInto, toMerge)
sidonaldson

20

Nó cũng có thể hữu ích để xem xét những gì họ làm từ quan điểm ngữ nghĩa:

_.chỉ định

   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)

_đặc biệt

   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.

Tôi tin rằng việc học cách nghĩ về các phương pháp đó theo quan điểm ngữ nghĩa sẽ cho phép bạn "đoán" tốt hơn hành vi của tất cả các kịch bản khác nhau của các giá trị hiện tại và không tồn tại.


3

Nếu bạn muốn có một bản sao sâu mà không ghi đè trong khi vẫn giữ nguyên objtham chiếu

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.