feat(angular.forEach): add the array/object as the 3rd param like the native array forEach

Closes #7902
This commit is contained in:
Jason Bedard
2014-06-15 13:09:59 -07:00
committed by Igor Minar
parent 41cec4d680
commit df9e60c8e7
2 changed files with 77 additions and 8 deletions

View File

@@ -210,9 +210,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.
@@ -240,22 +240,22 @@ 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 (isArray(obj) || isArrayLike(obj)) {
var isPrimitive = typeof obj !== 'object';
for (key = 0, length = obj.length; key < length; key++) {
if (isPrimitive || key in obj) {
iterator.call(context, obj[key], key);
iterator.call(context, obj[key], key, obj);
}
}
} else if (obj.forEach && obj.forEach !== forEach) {
obj.forEach(iterator, context);
obj.forEach(iterator, context, obj);
} else {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
iterator.call(context, obj[key], key);
iterator.call(context, obj[key], key, obj);
}
}
}

View File

@@ -654,10 +654,79 @@ describe('angular', function() {
var log = [];
var collection = [];
collection[5] = 'SPARSE';
forEach(collection, function (item, index) { log.push(item + index); });
forEach(collection, function (item, index) {
log.push(item + index);
});
expect(log.length).toBe(1);
expect(log[0]).toBe('SPARSE5');
});
describe('ES spec api compliance', function() {
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("<span>a</span><span>b</span>"));
});
it('should follow the ES spec when called with childNodes NodeList', function() {
testForEachSpec(2, jqLite("<p><span>a</span><span>b</span></p>")[0].childNodes);
});
it('should follow the ES spec when called with getElementsByTagName HTMLCollection', function() {
testForEachSpec(2, jqLite("<p><span>a</span><span>b</span></p>")[0].getElementsByTagName("*"));
});
it('should follow the ES spec when called with querySelectorAll HTMLCollection', function() {
testForEachSpec(2, jqLite("<p><span>a</span><span>b</span></p>")[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);
});
});
});