From bd96cf62ab7a2036b4baabc9f86607cd9779c68e Mon Sep 17 00:00:00 2001 From: Jason Bedard Date: Sun, 15 Jun 2014 13:09:59 -0700 Subject: [PATCH] fix(forEach): add the array/object as the 3rd param like the native array forEach --- src/Angular.js | 12 ++++++------ test/AngularSpec.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/Angular.js b/src/Angular.js index 905421e0fd03..454531de472d 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -208,9 +208,9 @@ function isArrayLike(obj) { * * @description * Invokes the `iterator` function once for each item in `obj` collection, which can be either an - * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value` - * is the value of an object property or an array element and `key` is the object property key or - * array element index. Specifying a `context` for the function is optional. + * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value` + * is the value of an object property or an array element, `key` is the object property key or + * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional. * * It is worth noting that `.forEach` does not iterate over inherited properties because it filters * using the `hasOwnProperty` method. @@ -238,19 +238,19 @@ function forEach(obj, iterator, context) { // Need to check if hasOwnProperty exists, // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) { - iterator.call(context, obj[key], key); + iterator.call(context, obj[key], key, obj); } } } else if (obj.forEach && obj.forEach !== forEach) { obj.forEach(iterator, context); } else if (isArrayLike(obj)) { for (key = 0, length = obj.length; key < length; key++) { - iterator.call(context, obj[key], key); + iterator.call(context, obj[key], key, obj); } } else { for (key in obj) { if (obj.hasOwnProperty(key)) { - iterator.call(context, obj[key], key); + iterator.call(context, obj[key], key, obj); } } } diff --git a/test/AngularSpec.js b/test/AngularSpec.js index 472ae6269913..82adc8baa7af 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -617,6 +617,51 @@ describe('angular', function() { forEach(obj, function(value, key) { log.push(key + ':' + value); }); expect(log).toEqual(['length:2', 'foo:bar']); }); + + function testForEachSpec(expectedSize, collection) { + var that = {}; + + forEach(collection, function(value, key, collectionArg) { + expect(collectionArg).toBe(collection); + expect(collectionArg[key]).toBe(value); + + expect(this).toBe(that); + + expectedSize--; + }, that); + + expect(expectedSize).toBe(0); + } + it('should follow the ES spec when called with array', function() { + testForEachSpec(2, [1,2]); + }); + it('should follow the ES spec when called with arguments', function() { + testForEachSpec(2, (function(){ return arguments; }(1,2))); + }); + it('should follow the ES spec when called with string', function() { + testForEachSpec(2, '12'); + }); + it('should follow the ES spec when called with jQuery/jqLite', function() { + testForEachSpec(2, jqLite("ab")); + }); + it('should follow the ES spec when called with childNodes NodeList', function() { + testForEachSpec(2, jqLite("

ab

")[0].childNodes); + }); + it('should follow the ES spec when called with getElementsByTagName HTMLCollection', function() { + testForEachSpec(2, jqLite("

ab

")[0].getElementsByTagName("*")); + }); + it('should follow the ES spec when called with querySelectorAll HTMLCollection', function() { + testForEachSpec(2, jqLite("

ab

")[0].querySelectorAll("*")); + }); + it('should follow the ES spec when called with JSON', function() { + testForEachSpec(2, {a: 1, b: 2}); + }); + it('should follow the ES spec when called with function', function() { + function f(){} + f.a = 1; + f.b = 2; + testForEachSpec(2, f); + }); });