Improve compliance with Array methods in ES5 when passed a null value. (ES5 Array methods treat null like [])

This commit is contained in:
Ryan W Tenney
2010-12-06 23:30:43 -05:00
parent e63f44f798
commit cdcf18b79c
3 changed files with 47 additions and 5 deletions

View File

@@ -83,6 +83,7 @@ $(document).ready(function() {
equals(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function');
var result = (function(){ return _.indexOf(arguments, 2); })(1, 2, 3);
equals(result, 1, 'works on an arguments object');
equals(_.indexOf(null, 2), -1, 'handles nulls properly');
});
test("arrays: lastIndexOf", function() {
@@ -92,6 +93,7 @@ $(document).ready(function() {
equals(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element');
var result = (function(){ return _.lastIndexOf(arguments, 1); })(1, 0, 1, 0, 0, 1, 0, 0, 0);
equals(result, 5, 'works on an arguments object');
equals(_.indexOf(null, 2), -1, 'handles nulls properly');
});
test("arrays: range", function() {

View File

@@ -29,6 +29,10 @@ $(document).ready(function() {
answers = [];
_.each({range : 1, speed : 2, length : 3}, function(v){ answers.push(v); });
ok(answers.join(', '), '1, 2, 3', 'can iterate over objects with numeric length properties');
answers = 0;
_.each(null, function(){ ++answers; });
equals(answers, 0, 'handles a null properly');
});
test('collections: map', function() {
@@ -46,6 +50,9 @@ $(document).ready(function() {
var ids = _.map(document.images, function(n){ return n.id; });
ok(ids[0] == 'chart_image', 'can use collection methods on HTMLCollections');
var ifnull = _.map(null, function(){});
ok(_.isArray(ifnull) && ifnull.length === 0, 'handles a null properly');
});
test('collections: reduce', function() {
@@ -64,6 +71,16 @@ $(document).ready(function() {
var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; });
equals(sum, 6, 'default initial value');
var ifnull;
try {
_.reduce(null, function(){});
} catch (ex) {
ifnull = ex;
}
ok(ifnull instanceof TypeError, 'handles a null (without inital value) properly');
ok(_.reduce(null, function(){}, 138) === 138, 'handles a null (with initial value) properly');
});
test('collections: reduceRight', function() {
@@ -75,6 +92,16 @@ $(document).ready(function() {
var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; });
equals(list, 'bazbarfoo', 'default initial value');
var ifnull;
try {
_.reduceRight(null, function(){});
} catch (ex) {
ifnull = ex;
}
ok(ifnull instanceof TypeError, 'handles a null (without inital value) properly');
ok(_.reduceRight(null, function(){}, 138) === 138, 'handles a null (with initial value) properly');
});
test('collections: detect', function() {

View File

@@ -68,6 +68,7 @@
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
var value;
if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (_.isNumber(obj.length)) {
@@ -86,8 +87,9 @@
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = function(obj, iterator, context) {
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
var results = [];
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
results[results.length] = iterator.call(context, value, index, list);
});
@@ -98,6 +100,7 @@
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = memo !== void 0;
if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
@@ -105,16 +108,19 @@
each(obj, function(value, index, list) {
if (!initial && index === 0) {
memo = value;
initial = true;
} else {
memo = iterator.call(context, memo, value, index, list);
}
});
if (!initial) throw new TypeError("Reduce of empty array with no initial value");
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
@@ -139,8 +145,9 @@
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, iterator, context) {
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
each(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) results[results.length] = value;
});
@@ -150,6 +157,7 @@
// Return all the elements for which a truth test fails.
_.reject = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
each(obj, function(value, index, list) {
if (!iterator.call(context, value, index, list)) results[results.length] = value;
});
@@ -161,8 +169,9 @@
// Aliased as `all`.
_.every = _.all = function(obj, iterator, context) {
iterator = iterator || _.identity;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
});
@@ -174,8 +183,9 @@
// Aliased as `any`.
var any = _.some = _.any = function(obj, iterator, context) {
iterator = iterator || _.identity;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
each(obj, function(value, index, list) {
if (result = iterator.call(context, value, index, list)) return breaker;
});
@@ -185,8 +195,9 @@
// Determine if a given value is included in the array or object using `===`.
// Aliased as `contains`.
_.include = _.contains = function(obj, target) {
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
var found = false;
if (obj == null) return found;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
any(obj, function(value) {
if (found = value === target) return true;
});
@@ -346,6 +357,7 @@
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
_.indexOf = function(array, item) {
if (array == null) return -1;
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
for (var i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
return -1;
@@ -354,6 +366,7 @@
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item) {
if (array == null) return -1;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
var i = array.length;
while (i--) if (array[i] === item) return i;