Skip to content

Commit b6fdbb9

Browse files
Merge pull request #2696 from SergioCrisostomo/fix-2613
iterate non-enumerables in old IE also
2 parents 7dd4a07 + 9f9128a commit b6fdbb9

File tree

4 files changed

+114
-48
lines changed

4 files changed

+114
-48
lines changed

Source/Core/Core.js

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -57,24 +57,35 @@ var instanceOf = this.instanceOf = function(item, object){
5757
return item instanceof object;
5858
};
5959

60-
// Function overloading
61-
62-
var Function = this.Function;
60+
var hasOwnProperty = Object.prototype.hasOwnProperty;
6361

62+
/*<ltIE8>*/
6463
var enumerables = true;
6564
for (var i in {toString: 1}) enumerables = null;
6665
if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
66+
function forEachObjectEnumberableKey(object, fn, bind) {
67+
if (enumerables) for (var i = enumerables.length; i--;){
68+
var k = enumberables[i];
69+
// signature has key-value, so overloadSetter can directly pass the
70+
// method function, without swapping arguments.
71+
if (hasOwnProperty.call(object, k)) fn.call(bind, k, object[k]);
72+
}
73+
}
74+
/*</ltIE8>*/
75+
76+
// Function overloading
77+
78+
var Function = this.Function;
6779

6880
Function.prototype.overloadSetter = function(usePlural){
6981
var self = this;
7082
return function(a, b){
7183
if (a == null) return this;
7284
if (usePlural || typeof a != 'string'){
7385
for (var k in a) self.call(this, k, a[k]);
74-
if (enumerables) for (var i = enumerables.length; i--;){
75-
k = enumerables[i];
76-
if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
77-
}
86+
/*<ltIE8>*/
87+
forEachObjectEnumberableKey(a, self, this);
88+
/*</ltIE8>*/
7889
} else {
7990
self.call(this, a, b);
8091
}
@@ -301,16 +312,7 @@ Number.extend('random', function(min, max){
301312
return Math.floor(Math.random() * (max - min + 1) + min);
302313
});
303314

304-
// forEach, each
305-
306-
var hasOwnProperty = Object.prototype.hasOwnProperty;
307-
Object.extend('forEach', function(object, fn, bind){
308-
for (var key in object){
309-
if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
310-
}
311-
});
312-
313-
Object.each = Object.forEach;
315+
// forEach, each, keys
314316

315317
Array.implement({
316318

@@ -329,6 +331,32 @@ Array.implement({
329331

330332
});
331333

334+
Object.extend({
335+
336+
keys: function(object){
337+
var keys = [];
338+
for (var k in a){
339+
if (hasOwnProperty.call(object, k)) keys.push(k);
340+
}
341+
/*<ltIE8>*/
342+
forEachObjectEnumberableKey(object, function(k){
343+
keys.push(k);
344+
});
345+
/*</ltIE8>*/
346+
return keys;
347+
},
348+
349+
forEach: function(object, fn, bind){
350+
Object.keys(object).forEach(function(key){
351+
fn.call(bind, object[key], key, object);
352+
});
353+
}
354+
355+
});
356+
357+
Object.each = Object.forEach;
358+
359+
332360
// Array & Object cloning, Object merging and appending
333361

334362
var cloneOf = function(item){

Source/Types/Object.js

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,47 +31,48 @@ Object.extend({
3131

3232
map: function(object, fn, bind){
3333
var results = {};
34-
for (var key in object){
35-
if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
34+
var keys = Object.keys(object);
35+
for (var i = 0; i < keys.length; i++){
36+
var key = keys[i];
37+
results[key] = fn.call(bind, object[key], key, object);
3638
}
3739
return results;
3840
},
3941

4042
filter: function(object, fn, bind){
4143
var results = {};
42-
for (var key in object){
43-
var value = object[key];
44-
if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value;
44+
var keys = Object.keys(object);
45+
for (var i = 0; i < keys.length; i++){
46+
var key = keys[i], value = object[key];
47+
if (fn.call(bind, value, key, object)) results[key] = value;
4548
}
4649
return results;
4750
},
4851

4952
every: function(object, fn, bind){
50-
for (var key in object){
51-
if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
53+
var keys = Object.keys(object);
54+
for (var i = 0; i < keys.length; i++){
55+
var key = keys[i];
56+
if (!fn.call(bind, object[key], key)) return false;
5257
}
5358
return true;
5459
},
5560

5661
some: function(object, fn, bind){
57-
for (var key in object){
58-
if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
62+
var keys = Object.keys(object);
63+
for (var i = 0; i < keys.length; i++){
64+
var key = keys[i];
65+
if (fn.call(bind, object[key], key)) return true;
5966
}
6067
return false;
6168
},
6269

63-
keys: function(object){
64-
var keys = [];
65-
for (var key in object){
66-
if (hasOwnProperty.call(object, key)) keys.push(key);
67-
}
68-
return keys;
69-
},
70-
7170
values: function(object){
7271
var values = [];
73-
for (var key in object){
74-
if (hasOwnProperty.call(object, key)) values.push(object[key]);
72+
var keys = Object.keys(object);
73+
for (var i = 0; i < keys.length; i++){
74+
var k = keys[i];
75+
values.push(object[k]);
7576
}
7677
return values;
7778
},
@@ -81,8 +82,10 @@ Object.extend({
8182
},
8283

8384
keyOf: function(object, value){
84-
for (var key in object){
85-
if (hasOwnProperty.call(object, key) && object[key] === value) return key;
85+
var keys = Object.keys(object);
86+
for (var i = 0; i < keys.length; i++){
87+
var key = keys[i];
88+
if (object[key] === value) return key;
8689
}
8790
return null;
8891
},

Specs/Core/Core.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,27 @@ describe('Type', function(){
779779

780780
});
781781

782+
describe('Object.keys', function(){
783+
784+
var object = { a: 'string', b: 233, c: {} };
785+
786+
it('keys should return an empty array', function(){
787+
expect(Object.keys({})).toEqual([]);
788+
});
789+
790+
it('should return an array containing the keys of the object', function(){
791+
expect(Object.keys(object)).toEqual(['a', 'b', 'c']);
792+
});
793+
794+
it('should return an array containing non-enum keys', function(){
795+
var buggy = {constructor: 'foo', valueOf: 'bar'};
796+
var keys = Object.keys(buggy).join('');
797+
expect(keys.indexOf('constructor') != -1).toBeTruthy();
798+
expect(keys.indexOf('valueOf') != -1).toBeTruthy();
799+
});
800+
801+
});
802+
782803
describe('Object.each', function(){
783804

784805
it('should call the function for each item in the object', function(){
@@ -790,6 +811,30 @@ describe('Object.each', function(){
790811
expect(daysObj).toEqual({first: 'Sunday', second: 'Monday', third: 'Tuesday'});
791812
});
792813

814+
it('should call non-enumerable properties too', function(){
815+
var obj = {
816+
foo: 'bar',
817+
constructor: "constructor",
818+
hasOwnProperty: "hasOwnProperty",
819+
isPrototypeOf: "isPrototypeOf",
820+
propertyIsEnumerable: "propertyIsEnumerable",
821+
toLocaleString: "toLocaleString",
822+
toString: "toString",
823+
valueOf: "valueOf"
824+
};
825+
826+
var keysInObject = true, iteration = 0;
827+
var props = ['foo', 'hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'].join('');
828+
829+
Object.each(obj, function(i, k){
830+
iteration++;
831+
if (props.indexOf(k) == -1) keysInObject = false;
832+
});
833+
834+
expect(keysInObject).toBeTruthy();
835+
expect(iteration).toEqual(8);
836+
});
837+
793838
});
794839

795840
describe('Array.each', function(){

Specs/Types/Object.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,6 @@ describe("Object Methods", function(){
7070
expect(Object.some(object, Type.isArray)).toBeFalsy();
7171
});
7272

73-
// Object.keys
74-
75-
it('keys should return an empty array', function(){
76-
expect(Object.keys({})).toEqual([]);
77-
});
78-
79-
it('should return an array containing the keys of the object', function(){
80-
expect(Object.keys(object)).toEqual(['a', 'b', 'c']);
81-
});
82-
8373
// Object.values
8474

8575
it('values should return an empty array', function(){

0 commit comments

Comments
 (0)