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

Commit b59b04f

Browse files
gentooboontoopetebacondarwin
authored andcommitted
fix(Angular.copy): preserve prototype chain when copying objects
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`.
1 parent ffdde26 commit b59b04f

File tree

2 files changed

+19
-6
lines changed

2 files changed

+19
-6
lines changed

src/Angular.js

+9-6
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,8 @@ function copy(source, destination, stackSource, stackDest) {
772772
} else if (isRegExp(source)) {
773773
destination = new RegExp(source.source);
774774
} else if (isObject(source)) {
775-
destination = copy(source, {}, stackSource, stackDest);
775+
var emptyObject = Object.create(Object.getPrototypeOf(source));
776+
destination = copy(source, emptyObject, stackSource, stackDest);
776777
}
777778
}
778779
} else {
@@ -807,12 +808,14 @@ function copy(source, destination, stackSource, stackDest) {
807808
delete destination[key];
808809
});
809810
for ( var key in source) {
810-
result = copy(source[key], null, stackSource, stackDest);
811-
if (isObject(source[key])) {
812-
stackSource.push(source[key]);
813-
stackDest.push(result);
811+
if(source.hasOwnProperty(key)) {
812+
result = copy(source[key], null, stackSource, stackDest);
813+
if (isObject(source[key])) {
814+
stackSource.push(source[key]);
815+
stackDest.push(result);
816+
}
817+
destination[key] = result;
814818
}
815-
destination[key] = result;
816819
}
817820
setHashKey(destination,h);
818821
}

test/AngularSpec.js

+10
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ describe('angular', function() {
2424
expect(copy([], arr)).toBe(arr);
2525
});
2626

27+
it("should preserve prototype chaining", function() {
28+
var GrandParentProto = {};
29+
var ParentProto = Object.create(GrandParentProto);
30+
var obj = Object.create(ParentProto);
31+
expect(ParentProto.isPrototypeOf(copy(obj))).toBe(true);
32+
expect(GrandParentProto.isPrototypeOf(copy(obj))).toBe(true);
33+
var Foo = function() {};
34+
expect(copy(new Foo()) instanceof Foo).toBe(true);
35+
});
36+
2737
it("should copy Date", function() {
2838
var date = new Date(123);
2939
expect(copy(date) instanceof Date).toBeTruthy();

0 commit comments

Comments
 (0)