Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Conversation

gentooboontoo
Copy link
Contributor

Instead of values being simply copied, this change allows to preserve
definitions of own properties. Thus, enumerable (or not), writable (or
not), getter/setter, etc. property definitions are preserved across copies. It
improves change from b59b04f preserving prototype chain.

As a side effect, it also includes previously ignored non-enumerable properties
by using Object.getOwnPropertyNames to iterate every own properties instead
of for ... in ... + hasOwnProperty.

Instead of values being simply copied, this change allows to preserve
definitions of own properties. Thus, enumerable (or not), writable (or
not), getter/setter, etc. property definitions are preserved across copies. It
improves change from b59b04f preserving prototype chain.

As a side effect, it also includes previously ignored non-enumerable properties
by using `Object.getOwnPropertyNames` to iterate **every** own properties instead
of `for ... in ...` + `hasOwnProperty`.
@mary-poppins
Copy link

Thanks for the PR! Please check the items below to help us merge this faster. See the contributing docs for more information.

  • Uses the issue template (#8034)

If you need to make changes to your pull request, you can update the commit with git commit --amend.
Then, update the pull request with git push -f.

Thanks again for your help!

gentooboontoo referenced this pull request Jul 1, 2014
So far, angular.copy was copying all properties including those from
prototype chain and was losing the whole prototype chain (except for Date,
Regexp, and Array).

Deep copy should exclude properties from the prototype chain because it
is useless to do so. When modified, properties from prototype chain are
overwritten on the object itself and will be deeply copied then.

Moreover, preserving prototype chain allows instanceof operator to be
consistent between the source object and the copy.
Before this change,

    var Foo = function() {};
    var foo = new Foo();
    var fooCopy = angular.copy(foo);
    foo instanceof Foo; // => true
    fooCopy instanceof Foo; // => false

Now,

    foo instanceof Foo; // => true
    fooCopy instanceof Foo; // => true

The new behaviour is useful when using $http transformResponse. When
receiving JSON data, we could transform it and instantiate real object
"types" from it. The transformed response is always copied by Angular.
The old behaviour was losing the whole prototype chain and broke all
"types" from third-party libraries depending on instanceof.

Closes #5063
Closes #3767
Closes #4996

BREAKING CHANGE:

This changes `angular.copy` so that it applies the prototype of the original
object to the copied object.  Previously, `angular.copy` would copy properties
of the original object's prototype chain directly onto the copied object.

This means that if you iterate over only the copied object's `hasOwnProperty`
properties, it will no longer contain the properties from the prototype.
This is actually much more reasonable behaviour and it is unlikely that
applications are actually relying on this.

If this behaviour is relied upon, in an app, then one should simply iterate
over all the properties on the object (and its inherited properties) and
not filter them with `hasOwnProperty`.

**Be aware that this change also uses a feature that is not compatible with
IE8.**  If you need this to work on IE8 then you would need to provide a polyfill
for `Object.create` and `Object.getPrototypeOf`.
@IgorMinar
Copy link
Contributor

sorry but angular.copy is primarily intended for the internal needs of the framework (and it was our mistake to expose it). having said that we don't have intentions to change the implementation to support use-cases not needed by the framework.

@IgorMinar IgorMinar closed this Jul 1, 2014
@gentooboontoo
Copy link
Contributor Author

@IgorMinar I agree angular.copy is not meant to be used externally. I never call it directly in my code (I don't need it). Unfortunately, issues arise when angular.copy is called internally by the framework.
Currently, angular.copy could not safely copy objects with values not being primitive types or plain objects (otherwise there is a loss of information that sometimes triggers bugs in application code).

This is not a real blocker for me. I was just trying to improve it. It didn't seem to introduce breaking changes at first and tests were all passing.

That said, thank you very much for your great work for Angular as a whole.

@petebacondarwin
Copy link
Contributor

@IgorMinar - I think we should relook at this PR. If we are copying objects in Angular we need to make sure that we copy everything.

@Narretz Narretz modified the milestones: 1.3.0, Backlog Jul 2, 2014
@petebacondarwin
Copy link
Contributor

Having discussed this with Igor, we feel that ensuring high fidelity when copying objects that contain dynamic properties adds too much overhead to the copy method which needs to be as fast and lightweight as possible.

We are going to consider copying non-enumerable value properties though, since not being enumerable does not necessarily imply that they should not be copied. But we need to ensure that this is done in a way that keeps their non-enumerability status while not impacting on the performance of copy. PRs welcome!

@zuzusik
Copy link
Contributor

zuzusik commented Feb 9, 2017

@petebacondarwin @IgorMinar I have open PR for this: #15693
(copying non-enumerable value properties)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants