Recursive/deep extend/assign in Underscore.js?
With Lodash (fork of underscore) you can. Lodash's _.extend method accept third (or higher) parameter to be a function, that receives values (old and new); So you can do something like this:
var deep = function(a, b) {
return _.isObject(a) && _.isObject(b) ? _.extend(a, b, deep) : b;
};
var a = {a:{b:{c:1}}},
b = {a:{b:{z:1}}};
_.extend(a,b,deep);
upd. As Paolo Moretti said in comments, there is the same function in lodash called _.merge:
_.merge(a,b);
jQuery has an extend() function, which does the same thing as its Underscore counterpart, but also has a deep argument which allows it to merge recursively as you desire:
var creditOperation = $.extend(true, baseOperation, {
query: {
'method': 'baz'
}
});
Or, if you don't want to overwrite baseOperation:
var creditOperation = $.extend(true, {}, baseOperation, {
query: {
'method': 'baz'
}
});
Underscore has no plans to add a deep extend since it's deemed too complicated to deal with different types of objects. Instead, users are encouraged to implement their own solutions with the support for what they need.
In your case it's only plain objects, so an implementation is quite straightforward:
_.deepObjectExtend = function(target, source) {
for (var prop in source)
if (prop in target)
_.deepObjectExtend(target[prop], source[prop]);
else
target[prop] = source[prop];
return target;
}
Stand-alone version of Bergi's deep extend, including the fix for when a value is a string instead of an object. Also patched to be more strict.
function deepObjectExtend (target, source) {
for (var prop in source) {
if (source.hasOwnProperty(prop)) {
if (target[prop] && typeof source[prop] === 'object') {
deepObjectExtend(target[prop], source[prop]);
}
else {
target[prop] = source[prop];
}
}
}
return target;
}
Kurt Milam has published a mixin that adds a deepExtend
method to underscore.js. It even deals with regular expressions (if you want).
Excerpt from the documentation:
Mix it in with underscore.js:
_.mixin({deepExtend: deepExtend});
Call it like this:
var myObj = _.deepExtend(grandparent, child, grandchild, greatgrandchild)
Notes: Keep it DRY.
This function is especially useful if you're working with JSON config documents. It allows you to create a default config document with the most common settings, then override those settings for specific cases. It accepts any number of objects as arguments, giving you fine-grained control over your config document hierarchy.