Ensure methods accepting a thisArg argument allow null values.

Former-commit-id: 368b943687291f0d7ed02304284ac076ef86e02b
This commit is contained in:
John-David Dalton
2012-09-22 15:39:37 -07:00
parent d8e3e823a7
commit 1ca26ce676
3 changed files with 72 additions and 16 deletions

View File

@@ -33,6 +33,7 @@
'stringClass',
'thisArg',
'toString',
'undefined',
'value',
// lesser used variables

View File

@@ -456,8 +456,8 @@
/**
* Reusable iterator options shared by
* `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, `map`,
* `reject`, `some`, and `sortBy`.
* `countBy`, `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`,
* `map`, `reject`, `some`, and `sortBy`.
*/
var baseIteratorOptions = {
'args': 'collection, callback, thisArg',
@@ -466,7 +466,7 @@
'if (!callback) {\n' +
' callback = identity\n' +
'}\n' +
'else if (thisArg) {\n' +
'else if (thisArg !== undefined) {\n' +
' callback = bindIterator(callback, thisArg)\n' +
'}',
'inLoop': 'if (callback(value, index, collection) === false) return result'
@@ -481,7 +481,7 @@
' var valueProp = callback;\n' +
' callback = function(value) { return value[valueProp] }\n' +
'}\n' +
'else if (thisArg) {\n' +
'else if (thisArg !== undefined) {\n' +
' callback = bindIterator(callback, thisArg)\n' +
'}',
'inLoop':
@@ -516,7 +516,7 @@
/** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */
var forEachIteratorOptions = {
'top': 'if (thisArg) callback = bindIterator(callback, thisArg)'
'top': 'if (thisArg !== undefined) callback = bindIterator(callback, thisArg)'
};
/** Reusable iterator options for `forIn` and `forOwn` */
@@ -549,7 +549,7 @@
'var isFunc = typeof callback == \'function\';\n' +
'if (!isFunc) {\n' +
' var props = concat.apply(ArrayProto, arguments)\n' +
'} else if (thisArg) {\n' +
'} else if (thisArg !== undefined) {\n' +
' callback = bindIterator(callback, thisArg)\n' +
'}',
'inLoop':
@@ -792,7 +792,7 @@
'arrayLikeClasses, ArrayProto, bind, bindIterator, compareAscending, concat, ' +
'forIn, hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, ' +
'isPlainObject, objectClass, objectTypes, nativeKeys, propertyIsEnumerable, ' +
'slice, stringClass, toString',
'slice, stringClass, toString, undefined',
'var callee = function(' + args + ') {\n' + iteratorTemplate(data) + '\n};\n' +
'return callee'
);
@@ -1862,7 +1862,7 @@
' if (prop in object) result[prop] = object[prop]\n' +
' }\n' +
'} else {\n' +
' if (thisArg) callback = bindIterator(callback, thisArg)',
' if (thisArg !== undefined) callback = bindIterator(callback, thisArg)',
'inLoop':
'if (callback(value, index, object)) result[index] = value',
'bottom': '}'
@@ -2184,7 +2184,7 @@
'init': 'accumulator',
'top':
'var noaccum = arguments.length < 3;\n' +
'if (thisArg) callback = bindIterator(callback, thisArg)',
'if (thisArg !== undefined) callback = bindIterator(callback, thisArg)',
'beforeLoop': {
'array': 'if (noaccum) result = iteratee[++index]'
},
@@ -2763,7 +2763,7 @@
}
return result;
}
if (thisArg) {
if (thisArg !== undefined) {
callback = bindIterator(callback, thisArg);
}
while (++index < length) {
@@ -2813,7 +2813,7 @@
}
return result;
}
if (thisArg) {
if (thisArg !== undefined) {
callback = bindIterator(callback, thisArg);
}
while (++index < length) {
@@ -3011,7 +3011,7 @@
high = array.length;
if (callback) {
if (thisArg) {
if (thisArg !== undefined) {
callback = bind(callback, thisArg);
}
value = callback(value);
@@ -3105,7 +3105,7 @@
}
if (!callback) {
callback = identity;
} else if (thisArg) {
} else if (thisArg !== undefined) {
callback = bindIterator(callback, thisArg);
}
while (++index < length) {
@@ -3954,10 +3954,11 @@
* // => also calls `mage.castSpell(n)` three times
*/
function times(n, callback, thisArg) {
n = +n || 0;
var index = -1,
result = Array(n || 0);
result = Array(n);
if (thisArg) {
if (thisArg !== undefined) {
while (++index < n) {
result[index] = callback.call(thisArg, index);
}

View File

@@ -1713,7 +1713,61 @@
}
});
ok(pass, methodName + ' allows falsey arguments');
ok(pass, '_.' + methodName + ' allows falsey arguments');
});
});
test('should handle `null` `thisArg` arguments', function() {
var thisArg,
array = ['a'],
callback = function() { thisArg = this; },
useStrict = Function('"use strict";return this')() === undefined;
var funcs = [
'countBy',
'every',
'filter',
'find',
'forEach',
'forIn',
'forOwn',
'groupBy',
'map',
'max',
'min',
'omit',
'pick',
'reduce',
'reduceRight',
'reject',
'some',
'sortBy',
'sortedIndex',
'times',
'uniq'
];
_.each(funcs, function(methodName) {
var func = _[methodName],
message = '_.' + methodName + ' handles `null` `thisArg` arguments';
thisArg = undefined;
if (/^reduce/.test(methodName)) {
func(array, callback, 0, null);
} else if (methodName == 'sortedIndex') {
func(array, 'a', callback, null);
} else if (methodName == 'times') {
func(1, callback, null);
} else {
func(array, callback, null);
}
if (useStrict) {
deepEqual(thisArg, null, message);
} else {
equal(thisArg, window, message);
}
});
});
}());