36
36
isUndefined: true,
37
37
isDefined: true,
38
38
isObject: true,
39
+ isBlankObject: true,
39
40
isString: true,
40
41
isNumber: true,
41
42
isDate: true,
175
176
splice = [ ] . splice ,
176
177
push = [ ] . push ,
177
178
toString = Object . prototype . toString ,
179
+ getPrototypeOf = Object . getPrototypeOf ,
178
180
ngMinErr = minErr ( 'ng' ) ,
179
181
180
182
/** @name angular */
@@ -267,12 +269,25 @@ function forEach(obj, iterator, context) {
267
269
}
268
270
} else if ( obj . forEach && obj . forEach !== forEach ) {
269
271
obj . forEach ( iterator , context , obj ) ;
270
- } else {
272
+ } else if ( isBlankObject ( obj ) ) {
273
+ // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
274
+ for ( key in obj ) {
275
+ iterator . call ( context , obj [ key ] , key , obj ) ;
276
+ }
277
+ } else if ( typeof obj . hasOwnProperty === 'function' ) {
278
+ // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
271
279
for ( key in obj ) {
272
280
if ( obj . hasOwnProperty ( key ) ) {
273
281
iterator . call ( context , obj [ key ] , key , obj ) ;
274
282
}
275
283
}
284
+ } else {
285
+ // Slow path for objects which do not have a method `hasOwnProperty`
286
+ for ( key in obj ) {
287
+ if ( hasOwnProperty . call ( obj , key ) ) {
288
+ iterator . call ( context , obj [ key ] , key , obj ) ;
289
+ }
290
+ }
276
291
}
277
292
}
278
293
return obj ;
@@ -498,6 +513,16 @@ function isObject(value) {
498
513
}
499
514
500
515
516
+ /**
517
+ * Determine if a value is an object with a null prototype
518
+ *
519
+ * @returns {boolean } True if `value` is an `Object` with a null prototype
520
+ */
521
+ function isBlankObject ( value ) {
522
+ return value !== null && typeof value === 'object' && ! getPrototypeOf ( value ) ;
523
+ }
524
+
525
+
501
526
/**
502
527
* @ngdoc function
503
528
* @name angular.isString
@@ -781,7 +806,7 @@ function copy(source, destination, stackSource, stackDest) {
781
806
destination = new RegExp ( source . source , source . toString ( ) . match ( / [ ^ \/ ] * $ / ) [ 0 ] ) ;
782
807
destination . lastIndex = source . lastIndex ;
783
808
} else if ( isObject ( source ) ) {
784
- var emptyObject = Object . create ( Object . getPrototypeOf ( source ) ) ;
809
+ var emptyObject = Object . create ( getPrototypeOf ( source ) ) ;
785
810
destination = copy ( source , emptyObject , stackSource , stackDest ) ;
786
811
}
787
812
}
@@ -800,7 +825,7 @@ function copy(source, destination, stackSource, stackDest) {
800
825
stackDest . push ( destination ) ;
801
826
}
802
827
803
- var result ;
828
+ var result , key ;
804
829
if ( isArray ( source ) ) {
805
830
destination . length = 0 ;
806
831
for ( var i = 0 ; i < source . length ; i ++ ) {
@@ -820,21 +845,40 @@ function copy(source, destination, stackSource, stackDest) {
820
845
delete destination [ key ] ;
821
846
} ) ;
822
847
}
823
- for ( var key in source ) {
824
- if ( source . hasOwnProperty ( key ) ) {
825
- result = copy ( source [ key ] , null , stackSource , stackDest ) ;
826
- if ( isObject ( source [ key ] ) ) {
827
- stackSource . push ( source [ key ] ) ;
828
- stackDest . push ( result ) ;
848
+ if ( isBlankObject ( source ) ) {
849
+ // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
850
+ for ( key in source ) {
851
+ putValue ( key , source [ key ] , destination , stackSource , stackDest ) ;
852
+ }
853
+ } else if ( source && typeof source . hasOwnProperty === 'function' ) {
854
+ // Slow path, which must rely on hasOwnProperty
855
+ for ( key in source ) {
856
+ if ( source . hasOwnProperty ( key ) ) {
857
+ putValue ( key , source [ key ] , destination , stackSource , stackDest ) ;
858
+ }
859
+ }
860
+ } else {
861
+ // Slowest path --- hasOwnProperty can't be called as a method
862
+ for ( key in source ) {
863
+ if ( hasOwnProperty . call ( source , key ) ) {
864
+ putValue ( key , source [ key ] , destination , stackSource , stackDest ) ;
829
865
}
830
- destination [ key ] = result ;
831
866
}
832
867
}
833
868
setHashKey ( destination , h ) ;
834
869
}
835
-
836
870
}
837
871
return destination ;
872
+
873
+ function putValue ( key , val , destination , stackSource , stackDest ) {
874
+ // No context allocation, trivial outer scope, easily inlined
875
+ var result = copy ( val , null , stackSource , stackDest ) ;
876
+ if ( isObject ( val ) ) {
877
+ stackSource . push ( val ) ;
878
+ stackDest . push ( result ) ;
879
+ }
880
+ destination [ key ] = result ;
881
+ }
838
882
}
839
883
840
884
/**
@@ -915,14 +959,14 @@ function equals(o1, o2) {
915
959
} else {
916
960
if ( isScope ( o1 ) || isScope ( o2 ) || isWindow ( o1 ) || isWindow ( o2 ) ||
917
961
isArray ( o2 ) || isDate ( o2 ) || isRegExp ( o2 ) ) return false ;
918
- keySet = { } ;
962
+ keySet = createMap ( ) ;
919
963
for ( key in o1 ) {
920
964
if ( key . charAt ( 0 ) === '$' || isFunction ( o1 [ key ] ) ) continue ;
921
965
if ( ! equals ( o1 [ key ] , o2 [ key ] ) ) return false ;
922
966
keySet [ key ] = true ;
923
967
}
924
968
for ( key in o2 ) {
925
- if ( ! keySet . hasOwnProperty ( key ) &&
969
+ if ( ! ( key in keySet ) &&
926
970
key . charAt ( 0 ) !== '$' &&
927
971
o2 [ key ] !== undefined &&
928
972
! isFunction ( o2 [ key ] ) ) return false ;
0 commit comments