web-dev-qa-db-ja.com

Lodash - .extend()/ .assign()と.merge()の違い

Lodash ライブラリで、誰かが merge および extend/assign についてのより良い説明を提供できます。

それでも簡単な質問ですが、答えは私を避けています。

427
JDillon522

これがextend/assignのしくみです。sourceの各プロパティについて、その値をそのままdestinationにコピーします。プロパティ値自体がオブジェクトである場合、それらのプロパティの再帰的なトラバースはありません。オブジェクト全体がソースから取得され、宛先に設定されます。

これがmergeのしくみです。sourceの各プロパティについて、そのプロパティがオブジェクト自身であるかどうかを確認します。それが再帰的に下がって、子オブジェクトプロパティをソースからデスティネーションにマッピングしようとします。そのため、基本的にオブジェクト階層をソースからデスティネーションにマージします。 extend/assignでは、ソースからターゲットへのプロパティの単純な1レベルコピーです。

これはこのクリスタルを明確にする簡単なJSBinです: http://jsbin.com/uXaqIMa/2/edit?js,console

これも例の中に配列を含むより複雑なバージョンです: http://jsbin.com/uXaqIMa/1/edit?js,console

538
Shital Shah

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は子オブジェクトをマージし、その他はルートレベルで上書きします
  • undefinedで値を上書きするのは_.assign_.extendだけです

テスト

それらはすべて同じようにルートでメンバーを扱います。

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

_.assignundefinedを処理しますが、他の人はそれをスキップします

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

注意すべきもう1つの違いは、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}

そのためmergeundefinedの値を定義済みの値にマージしません。

73
samz

意味的な観点からそれらが何をしているのかを検討することもまた役に立つかもしれません:

_。割り当てます

   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 is like assign but does not assign objects but replicates them instead.
  (deep copy)

_.デフォルト

   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.

セマンティックの観点からこれらの方法を考えることを学ぶことで、既存の値と存在しない値のすべての異なるシナリオの動作がどうなるかをより「推測」できるようになると思います。

20
epeleg

同じobj参照を保持しながら、オーバーライドなしのディープコピーが必要な場合

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

0
mbao01