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

Commit bd07e12

Browse files
committed
perf(copy): Use different arrays for different types
Use different arrays for different types. Remove forEach to clean the destination object.
1 parent 9efb0d5 commit bd07e12

File tree

1 file changed

+101
-78
lines changed

1 file changed

+101
-78
lines changed

src/Angular.js

+101-78
Original file line numberDiff line numberDiff line change
@@ -783,98 +783,121 @@ function arrayRemove(array, value) {
783783
</file>
784784
</example>
785785
*/
786-
function copy(source, destination, stackSource, stackDest) {
787-
if (isWindow(source) || isScope(source)) {
788-
throw ngMinErr('cpws',
789-
"Can't copy! Making copies of Window or Scope instances is not supported.");
790-
}
791-
if (isTypedArray(destination)) {
792-
throw ngMinErr('cpta',
793-
"Can't copy! TypedArray destination cannot be mutated.");
794-
}
795-
796-
if (!destination) {
797-
destination = source;
798-
if (isObject(source)) {
799-
var index;
800-
if (stackSource && (index = stackSource.indexOf(source)) !== -1) {
801-
return stackDest[index];
802-
}
803-
804-
// TypedArray, Date and RegExp have specific copy functionality and must be
805-
// pushed onto the stack before returning.
806-
// Array and other objects create the base object and recurse to copy child
807-
// objects. The array/object will be pushed onto the stack when recursed.
808-
if (isArray(source)) {
809-
return copy(source, [], stackSource, stackDest);
810-
} else if (isTypedArray(source)) {
811-
destination = new source.constructor(source);
812-
} else if (isDate(source)) {
813-
destination = new Date(source.getTime());
814-
} else if (isRegExp(source)) {
815-
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
816-
destination.lastIndex = source.lastIndex;
817-
} else {
818-
var emptyObject = Object.create(getPrototypeOf(source));
819-
return copy(source, emptyObject, stackSource, stackDest);
820-
}
821-
822-
if (stackDest) {
823-
stackSource.push(source);
824-
stackDest.push(destination);
825-
}
786+
function copy(source, destination) {
787+
var stackSource_obj = [];
788+
var stackDest_obj = [];
789+
var stackSource_arr = [];
790+
var stackDest_arr = [];
791+
var stackSource_ta = [];
792+
var stackDest_ta = [];
793+
var stackSource_date = [];
794+
var stackDest_date = [];
795+
var stackSource_regex = [];
796+
var stackDest_regex = [];
797+
return copy_(source, destination);
798+
799+
function copy_(source, destination) {
800+
if (isWindow(source) || isScope(source)) {
801+
throw ngMinErr('cpws',
802+
"Can't copy! Making copies of Window or Scope instances is not supported.");
826803
}
827-
} else {
828-
if (source === destination) throw ngMinErr('cpi',
829-
"Can't copy! Source and destination are identical.");
830-
831-
stackSource = stackSource || [];
832-
stackDest = stackDest || [];
833-
834-
if (isObject(source)) {
835-
stackSource.push(source);
836-
stackDest.push(destination);
804+
if (isTypedArray(destination)) {
805+
throw ngMinErr('cpta',
806+
"Can't copy! TypedArray destination cannot be mutated.");
837807
}
838808

839-
var result, key;
840-
if (isArray(source)) {
841-
destination.length = 0;
842-
for (var i = 0; i < source.length; i++) {
843-
destination.push(copy(source[i], null, stackSource, stackDest));
809+
if (!destination) {
810+
destination = source;
811+
if (isObject(source)) {
812+
var index;
813+
814+
// TypedArray, Date and RegExp have specific copy functionality and must be
815+
// pushed onto the stack before returning.
816+
// Array and other objects create the base object and recurse to copy child
817+
// objects. The array/object will be pushed onto the stack when recursed.
818+
if (isArray(source)) {
819+
if ((index = stackSource_arr.indexOf(source)) !== -1) {
820+
return stackDest_arr[index];
821+
}
822+
return copy_(source, []);
823+
} else if (isTypedArray(source)) {
824+
if ((index = stackSource_ta.indexOf(source)) !== -1) {
825+
return stackDest_ta[index];
826+
}
827+
destination = new source.constructor(source);
828+
stackSource_ta.push(source);
829+
stackDest_ta.push(destination);
830+
} else if (isDate(source)) {
831+
if ((index = stackSource_date.indexOf(source)) !== -1) {
832+
return stackDest_date[index];
833+
}
834+
destination = new Date(source.getTime());
835+
stackSource_date.push(source);
836+
stackDest_date.push(destination);
837+
} else if (isRegExp(source)) {
838+
if ((index = stackSource_regex.indexOf(source)) !== -1) {
839+
return stackDest_regex[index];
840+
}
841+
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
842+
destination.lastIndex = source.lastIndex;
843+
stackSource_regex.push(source);
844+
stackDest_regex.push(destination);
845+
} else {
846+
if ((index = stackSource_obj.indexOf(source)) !== -1) {
847+
return stackDest_obj[index];
848+
}
849+
var emptyObject = Object.create(getPrototypeOf(source));
850+
return copy_(source, emptyObject);
851+
}
844852
}
845853
} else {
846-
var h = destination.$$hashKey;
847-
if (isArray(destination)) {
854+
if (source === destination) throw ngMinErr('cpi',
855+
"Can't copy! Source and destination are identical.");
856+
857+
var result, key;
858+
if (isArray(source)) {
859+
stackSource_arr.push(source);
860+
stackDest_arr.push(destination);
848861
destination.length = 0;
849-
} else {
850-
forEach(destination, function(value, key) {
851-
delete destination[key];
852-
});
853-
}
854-
if (isBlankObject(source)) {
855-
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
856-
for (key in source) {
857-
destination[key] = copy(source[key], null, stackSource, stackDest);
862+
for (var i = 0; i < source.length; i++) {
863+
destination.push(copy_(source[i], null));
858864
}
859-
} else if (source && typeof source.hasOwnProperty === 'function') {
860-
// Slow path, which must rely on hasOwnProperty
861-
for (key in source) {
862-
if (source.hasOwnProperty(key)) {
863-
destination[key] = copy(source[key], null, stackSource, stackDest);
865+
} else {
866+
stackSource_obj.push(source);
867+
stackDest_obj.push(destination);
868+
var h = destination.$$hashKey;
869+
if (isArray(destination)) {
870+
destination.length = 0;
871+
} else {
872+
for (key in destination) {
873+
delete destination[key];
864874
}
865875
}
866-
} else {
867-
// Slowest path --- hasOwnProperty can't be called as a method
868-
for (key in source) {
869-
if (hasOwnProperty.call(source, key)) {
870-
destination[key] = copy(source[key], null, stackSource, stackDest);
876+
if (isBlankObject(source)) {
877+
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
878+
for (key in source) {
879+
destination[key] = copy_(source[key], null);
880+
}
881+
} else if (source && typeof source.hasOwnProperty === 'function') {
882+
// Slow path, which must rely on hasOwnProperty
883+
for (key in source) {
884+
if (source.hasOwnProperty(key)) {
885+
destination[key] = copy_(source[key], null);
886+
}
887+
}
888+
} else {
889+
// Slowest path --- hasOwnProperty can't be called as a method
890+
for (key in source) {
891+
if (hasOwnProperty.call(source, key)) {
892+
destination[key] = copy_(source[key], null);
893+
}
871894
}
872895
}
896+
setHashKey(destination,h);
873897
}
874-
setHashKey(destination,h);
875898
}
899+
return destination;
876900
}
877-
return destination;
878901
}
879902

880903
/**

0 commit comments

Comments
 (0)