diff --git a/src/Angular.js b/src/Angular.js index 0b8f03395218..781d4450488e 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -101,13 +101,15 @@ function forEach(obj, iterator, context) { } } else if (obj.forEach && obj.forEach !== forEach) { obj.forEach(iterator, context); - } else if (isObject(obj) && isNumber(obj.length)) { + } else if (isArrayLike(obj)) { for (key = 0; key < obj.length; key++) iterator.call(context, obj[key], key); } else { - for (key in obj) { - if (obj.hasOwnProperty(key)) { - iterator.call(context, obj[key], key); + if(obj.hasOwnProperty) { + for (key in obj) { + if (obj.hasOwnProperty(key)) { + iterator.call(context, obj[key], key); + } } } } @@ -348,6 +350,18 @@ function isArray(value) { return toString.apply(value) == '[object Array]'; } +function isArrayLike(obj) { + var length = obj.length; + if (isWindow(obj)) { + return false; + } + return isArray(obj) || + ( + isObject(obj) && + isNumber(length) && + (length === 0 || length > 0 && (length - 1) in obj) + ); +} /** * @ngdoc function diff --git a/test/AngularSpec.js b/test/AngularSpec.js index 09bc902fec73..04f1c2d81f1b 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -267,6 +267,64 @@ describe('angular', function() { expect(log).toEqual(['bar:barVal', 'baz:bazVal']); }); + + it('should iterate identify and iterate array like objects', function() { + var log; + + log = []; + forEach( + [1,2,3], + function(value, key) { log.push(key + ':' + value) } + ); + expect(log).toEqual(['0:1', '1:2', '2:3']); + + log = []; + forEach( + { + 'bar' : 'bar', + 'length': 2 + }, + function(value, key) { log.push(key + ':' + value) } + ); + expect(log).toEqual(['bar:bar', 'length:2']); + + log = []; + forEach( + jqLite("

s1s2

").find("span"), + function(value, key) { log.push(key + ':' + value.innerHTML) } + ); + expect(log).toEqual(['0:s1', '1:s2']); + + log = []; + forEach( + jqLite("

s1s2

").find("b"), + function(value, key) { log.push(key + ':' + value.innerHTML); } + ); + expect(log.length).toBe(0); + + log = []; + forEach( + document.getElementsByTagName("x"), + function(value, key) { log.push(true) } + ); + expect(log.length).toBe(0); + + log = []; + forEach( + window, + function(value, key) { log.push(true) } + ); + expect(log.length).toBeGreaterThan(0); + + //a plain old object with length property with 0 value is treated like an array + log = []; + forEach( + {"prop1":"value1","length":0}, + function(value, key) { log.push(true) } + ); + expect(log.length).toBe(0); + + }); });