3636 isUndefined: true,
3737 isDefined: true,
3838 isObject: true,
39+ isBlankObject: true,
3940 isString: true,
4041 isNumber: true,
4142 isDate: true,
172173 splice = [ ] . splice ,
173174 push = [ ] . push ,
174175 toString = Object . prototype . toString ,
176+ getPrototypeOf = Object . getPrototypeOf ,
175177 ngMinErr = minErr ( 'ng' ) ,
176178
177179 /** @name angular */
@@ -461,6 +463,16 @@ function isObject(value) {
461463}
462464
463465
466+ /**
467+ * Determine if a value is an object with a null prototype
468+ *
469+ * @returns {boolean } True if `value` is an `Object` with a null prototype
470+ */
471+ function isBlankObject ( value ) {
472+ return value !== null && typeof value === 'object' && ! getPrototypeOf ( value ) ;
473+ }
474+
475+
464476/**
465477 * @ngdoc function
466478 * @name angular.isString
@@ -742,7 +754,7 @@ function copy(source, destination, stackSource, stackDest) {
742754 destination = new RegExp ( source . source , source . toString ( ) . match ( / [ ^ \/ ] * $ / ) [ 0 ] ) ;
743755 destination . lastIndex = source . lastIndex ;
744756 } else if ( isObject ( source ) ) {
745- var emptyObject = Object . create ( Object . getPrototypeOf ( source ) ) ;
757+ var emptyObject = Object . create ( getPrototypeOf ( source ) ) ;
746758 destination = copy ( source , emptyObject , stackSource , stackDest ) ;
747759 }
748760 }
@@ -761,7 +773,7 @@ function copy(source, destination, stackSource, stackDest) {
761773 stackDest . push ( destination ) ;
762774 }
763775
764- var result ;
776+ var result , key ;
765777 if ( isArray ( source ) ) {
766778 destination . length = 0 ;
767779 for ( var i = 0 ; i < source . length ; i ++ ) {
@@ -781,21 +793,40 @@ function copy(source, destination, stackSource, stackDest) {
781793 delete destination [ key ] ;
782794 } ) ;
783795 }
784- for ( var key in source ) {
785- if ( source . hasOwnProperty ( key ) ) {
786- result = copy ( source [ key ] , null , stackSource , stackDest ) ;
787- if ( isObject ( source [ key ] ) ) {
788- stackSource . push ( source [ key ] ) ;
789- stackDest . push ( result ) ;
796+ if ( isBlankObject ( source ) ) {
797+ // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
798+ for ( key in source ) {
799+ putValue ( key , source [ key ] , destination , stackSource , stackDest ) ;
800+ }
801+ } else if ( source && typeof source . hasOwnProperty === 'function' ) {
802+ // Slow path, which must rely on hasOwnProperty
803+ for ( key in source ) {
804+ if ( source . hasOwnProperty ( key ) ) {
805+ putValue ( key , source [ key ] , destination , stackSource , stackDest ) ;
806+ }
807+ }
808+ } else {
809+ // Slowest path --- hasOwnProperty can't be called as a method
810+ for ( key in source ) {
811+ if ( hasOwnProperty . call ( source , key ) ) {
812+ putValue ( key , source [ key ] , destination , stackSource , stackDest ) ;
790813 }
791- destination [ key ] = result ;
792814 }
793815 }
794816 setHashKey ( destination , h ) ;
795817 }
796-
797818 }
798819 return destination ;
820+
821+ function putValue ( key , val , destination , stackSource , stackDest ) {
822+ // No context allocation, trivial outer scope, easily inlined
823+ var result = copy ( val , null , stackSource , stackDest ) ;
824+ if ( isObject ( val ) ) {
825+ stackSource . push ( val ) ;
826+ stackDest . push ( result ) ;
827+ }
828+ destination [ key ] = result ;
829+ }
799830}
800831
801832/**
0 commit comments