Lodash ライブラリで、誰かが merge および extend/assign についてのより良い説明を提供できます。
それでも簡単な質問ですが、答えは私を避けています。
これが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
_.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" }
_.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" ]
注意すべきもう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}
そのためmerge
はundefined
の値を定義済みの値にマージしません。
意味的な観点からそれらが何をしているのかを検討することもまた役に立つかもしれません:
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.
works like _defaults but like merge will not simply copy objects
and will use recursion instead.
セマンティックの観点からこれらの方法を考えることを学ぶことで、既存の値と存在しない値のすべての異なるシナリオの動作がどうなるかをより「推測」できるようになると思います。
同じobj
参照を保持しながら、オーバーライドなしのディープコピーが必要な場合
obj = _.assign(obj, _.merge(obj, [source]))