feat(extend): optionally deep-extend when last parameter is `true` #10519

Closed
wants to merge 5 commits into
from

Projects

None yet

10 participants

@caitp
Contributor
caitp commented Dec 18, 2014

Same as #10507 but with my review comments addressed and jscs fixed.

Someone quickly review this so we can land it :<

@googlebot

We found a Contributor License Agreement for you (the sender of this pull request) and all commit authors, but as best as we can tell these commits were authored by someone else. If that's the case, please add them to this pull request and have them confirm that they're okay with these commits being contributed to Google. If we're mistaken and you did author these commits, just reply here to confirm.

@googlebot googlebot added the cla: no label Dec 18, 2014
@petebacondarwin petebacondarwin and 1 other commented on an outdated diff Dec 18, 2014
src/Angular.js
@@ -333,7 +333,7 @@ function setHashKey(obj, h) {
* Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
* by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
- * Note: Keep in mind that `angular.extend` does not support recursive merge (deep copy).
+ * Note: Pass `true` in as the last argument to perform a recursive merge (deep copy).
@petebacondarwin
petebacondarwin Dec 18, 2014 Member

We ought to have a @param tag for this but I am not sure how that fits with the variable argument src.

@caitp
caitp Dec 18, 2014 Contributor

yeah, it's not super clear how that would work... Maybe you could hack dgeni to process arguments that end with ... (or begin with ...) as a spread of parameters or something

@caitp
caitp Dec 18, 2014 Contributor

Bloody autocorrect renames "dgeni" to "degree" -.-

@petebacondarwin petebacondarwin and 1 other commented on an outdated diff Dec 18, 2014
src/Angular.js
@@ -341,19 +341,34 @@ function setHashKey(obj, h) {
*/
function extend(dst) {
var h = dst.$$hashKey;
+ var argsLength = arguments.length;
+ var isDeep = false;
+ if (argsLength >= 3) {
+ var maybeIsDeep = arguments[argsLength - 1];
+ // Secret code to use deep extend without adding hash keys to object properties!
@petebacondarwin
petebacondarwin Dec 18, 2014 Member

should this comment be:

// Secret code to use deep extend without adding hash keys to source object properties!
@caitp
caitp Dec 18, 2014 Contributor

it would be added to the destination object, I'll clarify that

@petebacondarwin petebacondarwin commented on an outdated diff Dec 18, 2014
src/Angular.js
var obj = arguments[i];
- if (obj) {
- var keys = Object.keys(obj);
- for (var j = 0, jj = keys.length; j < jj; j++) {
- var key = keys[j];
- dst[key] = obj[key];
+ if (!isObject(obj) && !isFunction(obj)) continue;
+ var keys = Object.keys(obj);
+ for (var j = 0, jj = keys.length; j < jj; j++) {
+ var key = keys[j];
+ var src = obj[key];
+
+ if (isDeep && isObject(src)) {
+ if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
+ extend(dst[key], src, 0xFACECAFE);
@petebacondarwin
petebacondarwin Dec 18, 2014 Member

What about object cycles? At least, I think we should put something in the docs about not trying to deep extend an object that has cyclic references.

@lgalfaso
Member

there is something odd in having an optional parameter after a variable number of parameters... why not put this optional parameter second?

@petebacondarwin petebacondarwin commented on an outdated diff Dec 18, 2014
test/AngularSpec.js
+ });
+ });
+
+
+ it('should not deep extend function-valued sources when deep extending', function() {
+ function fn() {}
+ var dst = { foo: 1 };
+ var src = { foo: fn };
+ extend(dst, src, true);
+ expect(dst).toEqual({
+ foo: fn
+ });
+ });
+
+
+ it('should create an empty array if destination property is a non-object and source property is an array when deep-extending', function() {
@petebacondarwin
petebacondarwin Dec 18, 2014 Member

'should create a new array ...'?

@petebacondarwin
Member

Apart from the minor docs/comments notes I made this looks good to me.

@caitp
Contributor
caitp commented Dec 18, 2014

there is something odd in having an optional parameter after a variable number of parameters... why not put this optional parameter second?

It would be lovely to do it that way, but it would be a breaking change... This is what I didn't like about the PR in the first place, but it doesn't seem "that bad" really

@lgalfaso
Member

Why would it be a breaking change?

there is something odd in having an optional parameter after a variable
number of parameters... why not put this optional parameter second?

It would be lovely to do it that way, but it would be a breaking change...
This is what I didn't like about the PR in the first place, but it doesn't
seem "that bad" really


Reply to this email directly or view it on GitHub
#10519 (comment).

caitp added some commits Dec 18, 2014
@caitp caitp refactor(extend): apply caitp's review comments for good measure :)
Derpity doot

Closes #10507
f683061
@caitp caitp style(Angular.js): make jscs happy
550f4ac
@petebacondarwin petebacondarwin modified the milestone: 1.3.8 Dec 18, 2014
@gkalpak gkalpak and 2 others commented on an outdated diff Dec 19, 2014
src/Angular.js
}
}
}
- setHashKey(dst, h);
+ if (isDeep !== 0xFACECAFE) setHashKey(dst, h);
@gkalpak
gkalpak Dec 19, 2014 Member

We should probably still attach a hashkey if h is defined.

@caitp
caitp Dec 20, 2014 Contributor

yeah, I think that makes sense, will get rid of FACECAFE =)

@OMarohn
OMarohn Dec 20, 2014

Ich bin bis 05.01.2015 abwesend.

Ich werde vertreten durch Herrn T. Landgraf und Herrn D. Neander.

Hinweis: Dies ist eine automatische Antwort auf Ihre Nachricht "Re:
[angular.js] feat(extend): optionally deep-extend when last parameter is
true (#10519)" gesendet am 20.12.2014 03:58:31.

Diese ist die einzige Benachrichtigung, die Sie empfangen werden, während
diese Person abwesend ist.=

@btford btford modified the milestone: 1.3.8, 1.3.9 Dec 19, 2014
@gdi2290
Member
gdi2290 commented Dec 20, 2014

I rather have angular.extend and angular.merge (for deep-extend)

@caitp caitp feat(Angular.js): implement angular.merge() helper
angular.merge() is like angular.extend(), but will "deeply" copy object properties from source
objects.
b3f74d0
@caitp
Contributor
caitp commented Dec 20, 2014

@gdi2290 I've implemented your suggestion --- It's slightly more code but it doesn't really make a big difference, so I'm fine with either one or neither

@caitp caitp perf(extend/merge): explicitly ignore hashKey property
Previously, hashKeys were copied from source objects and subsequently deleted or replaced with the
original. Now, there is no need to delete properties (which can be expensive), and no need to track
the initial hashKey value.
090e9b1
@gdi2290
Member
gdi2290 commented Dec 20, 2014

@caitp thanks, in the merge tests you should probably remove the third argument

merge(dst, src, true);
@caitp
Contributor
caitp commented Dec 20, 2014

heh missed a few --- but they won't matter anyway, it's good to have them even to verify it won't throw on primitives :>

@petebacondarwin
Member

I'll tweak the tests and land this today.

@petebacondarwin petebacondarwin self-assigned this Mar 3, 2015
@caitp caitp added a commit that closed this pull request Mar 3, 2015
@caitp @petebacondarwin caitp + petebacondarwin feat(angular.merge): provide an alternative to `angular.extend` that …
…merges 'deeply'

Closes #10507
Closes #10519
c0498d4
@caitp caitp closed this in c0498d4 Mar 3, 2015
@hansmaad hansmaad pushed a commit to hansmaad/angular.js that referenced this pull request Mar 10, 2015
@caitp caitp + hansmaad feat(angular.merge): provide an alternative to `angular.extend` that …
…merges 'deeply'

Closes #10507
Closes #10519
b794975
@netman92 netman92 added a commit to netman92/angular.js that referenced this pull request Aug 8, 2015
@caitp @netman92 caitp + netman92 feat(angular.merge): provide an alternative to `angular.extend` that …
…merges 'deeply'

Closes #10507
Closes #10519
558b2b9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment