mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-10 19:07:49 +00:00
Ensure isXYZ methods return boolean values and almost all methods allow falsey arguments.
Former-commit-id: a842eaf2fd262bed03df4a71b560b91801b7a75f
This commit is contained in:
149
lodash.js
149
lodash.js
@@ -379,15 +379,15 @@
|
|||||||
// iterate own properties using `Object.keys` if it's fast
|
// iterate own properties using `Object.keys` if it's fast
|
||||||
' <% if (isKeysFast && useHas) { %>\n' +
|
' <% if (isKeysFast && useHas) { %>\n' +
|
||||||
' var ownIndex = -1,\n' +
|
' var ownIndex = -1,\n' +
|
||||||
' ownProps = nativeKeys(iteratee),\n' +
|
' ownProps = objectTypes[typeof iteratee] ? nativeKeys(iteratee) : [],\n' +
|
||||||
' length = ownProps.length;\n\n' +
|
' length = ownProps.length;\n\n' +
|
||||||
' <%= objectBranch.beforeLoop %>;\n' +
|
' <%= objectBranch.beforeLoop %>;\n' +
|
||||||
' while (++ownIndex < length) {\n' +
|
' while (++ownIndex < length) {\n' +
|
||||||
' index = ownProps[ownIndex];\n' +
|
' index = ownProps[ownIndex];\n' +
|
||||||
' if (!(skipProto && index == \'prototype\')) {\n' +
|
' <% if (!hasDontEnumBug) { %>if (!(skipProto && index == \'prototype\')) {\n <% } %>' +
|
||||||
' value = iteratee[index];\n' +
|
' value = iteratee[index];\n' +
|
||||||
' <%= objectBranch.inLoop %>\n' +
|
' <%= objectBranch.inLoop %>\n' +
|
||||||
' }\n' +
|
' <% if (!hasDontEnumBug) { %>}\n<% } %>' +
|
||||||
' }' +
|
' }' +
|
||||||
|
|
||||||
// else using a for-in loop
|
// else using a for-in loop
|
||||||
@@ -946,7 +946,6 @@
|
|||||||
*/
|
*/
|
||||||
var shimKeys = createIterator({
|
var shimKeys = createIterator({
|
||||||
'args': 'object',
|
'args': 'object',
|
||||||
'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()',
|
|
||||||
'init': '[]',
|
'init': '[]',
|
||||||
'inLoop': 'result.push(index)'
|
'inLoop': 'result.push(index)'
|
||||||
});
|
});
|
||||||
@@ -1231,7 +1230,7 @@
|
|||||||
* // => true
|
* // => true
|
||||||
*/
|
*/
|
||||||
function has(object, property) {
|
function has(object, property) {
|
||||||
return hasOwnProperty.call(object, property);
|
return object ? hasOwnProperty.call(object, property) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1282,7 +1281,7 @@
|
|||||||
* // => true
|
* // => true
|
||||||
*/
|
*/
|
||||||
function isElement(value) {
|
function isElement(value) {
|
||||||
return !!(value && value.nodeType == 1);
|
return value ? value.nodeType == 1 : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1547,7 +1546,7 @@
|
|||||||
// http://es5.github.com/#x8
|
// http://es5.github.com/#x8
|
||||||
// and avoid a V8 bug
|
// and avoid a V8 bug
|
||||||
// http://code.google.com/p/v8/issues/detail?id=2291
|
// http://code.google.com/p/v8/issues/detail?id=2291
|
||||||
return value && objectTypes[typeof value];
|
return value ? objectTypes[typeof value] : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1686,12 +1685,74 @@
|
|||||||
* // => ['one', 'two', 'three'] (order is not guaranteed)
|
* // => ['one', 'two', 'three'] (order is not guaranteed)
|
||||||
*/
|
*/
|
||||||
var keys = !nativeKeys ? shimKeys : function(object) {
|
var keys = !nativeKeys ? shimKeys : function(object) {
|
||||||
|
var type = typeof object;
|
||||||
|
|
||||||
// avoid iterating over the `prototype` property
|
// avoid iterating over the `prototype` property
|
||||||
return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype')
|
if (type == 'function' && propertyIsEnumerable.call(object, 'prototype')) {
|
||||||
? shimKeys(object)
|
return shimKeys(object);
|
||||||
: nativeKeys(object);
|
}
|
||||||
|
return object && objectTypes[type]
|
||||||
|
? nativeKeys(object)
|
||||||
|
: [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges enumerable properties of the source object(s) into the `destination`
|
||||||
|
* object. Subsequent sources will overwrite propery assignments of previous
|
||||||
|
* sources.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
* @memberOf _
|
||||||
|
* @category Objects
|
||||||
|
* @param {Object} object The destination object.
|
||||||
|
* @param {Object} [source1, source2, ...] The source objects.
|
||||||
|
* @param {Object} [indicator] Internally used to indicate that the `stack`
|
||||||
|
* argument is an array of traversed objects instead of another source object.
|
||||||
|
* @param {Array} [stack=[]] Internally used to keep track of traversed objects
|
||||||
|
* to avoid circular references.
|
||||||
|
* @returns {Object} Returns the destination object.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* var stooges = [
|
||||||
|
* { 'name': 'moe' },
|
||||||
|
* { 'name': 'larry' }
|
||||||
|
* ];
|
||||||
|
*
|
||||||
|
* var ages = [
|
||||||
|
* { 'age': 40 },
|
||||||
|
* { 'age': 50 }
|
||||||
|
* ];
|
||||||
|
*
|
||||||
|
* _.merge(stooges, ages);
|
||||||
|
* // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }]
|
||||||
|
*/
|
||||||
|
var merge = createIterator(extendIteratorOptions, {
|
||||||
|
'args': 'object, source, indicator, stack',
|
||||||
|
'top':
|
||||||
|
'var destValue, found, isArr, stackLength, recursive = indicator == isPlainObject;\n' +
|
||||||
|
'if (!recursive) stack = [];\n' +
|
||||||
|
'for (var argsIndex = 1, argsLength = recursive ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n' +
|
||||||
|
' if (iteratee = arguments[argsIndex]) {',
|
||||||
|
'inLoop':
|
||||||
|
'if (value && ((isArr = isArray(value)) || isPlainObject(value))) {\n' +
|
||||||
|
' found = false; stackLength = stack.length;\n' +
|
||||||
|
' while (stackLength--) {\n' +
|
||||||
|
' if (found = stack[stackLength].source == value) break\n' +
|
||||||
|
' }\n' +
|
||||||
|
' if (found) {\n' +
|
||||||
|
' result[index] = stack[stackLength].value\n' +
|
||||||
|
' } else {\n' +
|
||||||
|
' destValue = (destValue = result[index]) && isArr\n' +
|
||||||
|
' ? (isArray(destValue) ? destValue : [])\n' +
|
||||||
|
' : (isPlainObject(destValue) ? destValue : {});\n' +
|
||||||
|
' stack.push({ value: destValue, source: value });\n' +
|
||||||
|
' result[index] = callee(destValue, value, isPlainObject, stack)\n' +
|
||||||
|
' }\n' +
|
||||||
|
'} else if (value != null) {\n' +
|
||||||
|
' result[index] = value\n' +
|
||||||
|
'}'
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a shallow clone of `object` composed of the specified properties.
|
* Creates a shallow clone of `object` composed of the specified properties.
|
||||||
* Property names may be specified as individual arguments or as arrays of
|
* Property names may be specified as individual arguments or as arrays of
|
||||||
@@ -1709,11 +1770,14 @@
|
|||||||
* // => { 'name': 'moe', 'age': 40 }
|
* // => { 'name': 'moe', 'age': 40 }
|
||||||
*/
|
*/
|
||||||
function pick(object) {
|
function pick(object) {
|
||||||
|
var result = {};
|
||||||
|
if (!object) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
var prop,
|
var prop,
|
||||||
index = 0,
|
index = 0,
|
||||||
props = concat.apply(ArrayProto, arguments),
|
props = concat.apply(ArrayProto, arguments),
|
||||||
length = props.length,
|
length = props.length;
|
||||||
result = {};
|
|
||||||
|
|
||||||
// start `index` at `1` to skip `object`
|
// start `index` at `1` to skip `object`
|
||||||
while (++index < length) {
|
while (++index < length) {
|
||||||
@@ -2025,63 +2089,6 @@
|
|||||||
*/
|
*/
|
||||||
var map = createIterator(baseIteratorOptions, mapIteratorOptions);
|
var map = createIterator(baseIteratorOptions, mapIteratorOptions);
|
||||||
|
|
||||||
/**
|
|
||||||
* Merges enumerable properties of the source object(s) into the `destination`
|
|
||||||
* object. Subsequent sources will overwrite propery assignments of previous
|
|
||||||
* sources.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
* @memberOf _
|
|
||||||
* @category Objects
|
|
||||||
* @param {Object} object The destination object.
|
|
||||||
* @param {Object} [source1, source2, ...] The source objects.
|
|
||||||
* @param {Object} [indicator] Internally used to indicate that the `stack`
|
|
||||||
* argument is an array of traversed objects instead of another source object.
|
|
||||||
* @param {Array} [stack=[]] Internally used to keep track of traversed objects
|
|
||||||
* to avoid circular references.
|
|
||||||
* @returns {Object} Returns the destination object.
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* var stooges = [
|
|
||||||
* { 'name': 'moe' },
|
|
||||||
* { 'name': 'larry' }
|
|
||||||
* ];
|
|
||||||
*
|
|
||||||
* var ages = [
|
|
||||||
* { 'age': 40 },
|
|
||||||
* { 'age': 50 }
|
|
||||||
* ];
|
|
||||||
*
|
|
||||||
* _.merge(stooges, ages);
|
|
||||||
* // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }]
|
|
||||||
*/
|
|
||||||
var merge = createIterator(extendIteratorOptions, {
|
|
||||||
'args': 'object, source, indicator, stack',
|
|
||||||
'top':
|
|
||||||
'var destValue, found, isArr, stackLength, recursive = indicator == isPlainObject;\n' +
|
|
||||||
'if (!recursive) stack = [];\n' +
|
|
||||||
'for (var argsIndex = 1, argsLength = recursive ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n' +
|
|
||||||
' if (iteratee = arguments[argsIndex]) {',
|
|
||||||
'inLoop':
|
|
||||||
'if (value && ((isArr = isArray(value)) || isPlainObject(value))) {\n' +
|
|
||||||
' found = false; stackLength = stack.length;\n' +
|
|
||||||
' while (stackLength--) {\n' +
|
|
||||||
' if (found = stack[stackLength].source == value) break\n' +
|
|
||||||
' }\n' +
|
|
||||||
' if (found) {\n' +
|
|
||||||
' result[index] = stack[stackLength].value\n' +
|
|
||||||
' } else {\n' +
|
|
||||||
' destValue = (destValue = result[index]) && isArr\n' +
|
|
||||||
' ? (isArray(destValue) ? destValue : [])\n' +
|
|
||||||
' : (isPlainObject(destValue) ? destValue : {});\n' +
|
|
||||||
' stack.push({ value: destValue, source: value });\n' +
|
|
||||||
' result[index] = callee(destValue, value, isPlainObject, stack)\n' +
|
|
||||||
' }\n' +
|
|
||||||
'} else if (value != null) {\n' +
|
|
||||||
' result[index] = value\n' +
|
|
||||||
'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the value of a specified property from all elements in
|
* Retrieves the value of a specified property from all elements in
|
||||||
* the `collection`.
|
* the `collection`.
|
||||||
|
|||||||
139
test/test.js
139
test/test.js
@@ -817,6 +817,50 @@
|
|||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
_.each([
|
||||||
|
'isArguments',
|
||||||
|
'isArray',
|
||||||
|
'isBoolean',
|
||||||
|
'isDate',
|
||||||
|
'isElement',
|
||||||
|
'isEmpty',
|
||||||
|
'isEqual',
|
||||||
|
'isFinite',
|
||||||
|
'isFunction',
|
||||||
|
'isNaN',
|
||||||
|
'isNull',
|
||||||
|
'isNumber',
|
||||||
|
'isObject',
|
||||||
|
'isRegExp',
|
||||||
|
'isString',
|
||||||
|
'isUndefined'
|
||||||
|
], function(methodName) {
|
||||||
|
var func = _[methodName];
|
||||||
|
QUnit.module('lodash.' + methodName + ' result');
|
||||||
|
|
||||||
|
test('should return a boolean', function() {
|
||||||
|
var expected = 'boolean';
|
||||||
|
|
||||||
|
equal(typeof func(arguments), expected);
|
||||||
|
equal(typeof func([]), expected);
|
||||||
|
equal(typeof func(true), expected);
|
||||||
|
equal(typeof func(false), expected);
|
||||||
|
equal(typeof func(new Date), expected);
|
||||||
|
equal(typeof func(window.document && document.body), expected);
|
||||||
|
equal(typeof func({}), expected);
|
||||||
|
equal(typeof func(undefined), expected);
|
||||||
|
equal(typeof func(Infinity), expected);
|
||||||
|
equal(typeof func(_), expected);
|
||||||
|
equal(typeof func(NaN), expected);
|
||||||
|
equal(typeof func(null), expected);
|
||||||
|
equal(typeof func(0), expected);
|
||||||
|
equal(typeof func({ 'a': 1 }), expected);
|
||||||
|
equal(typeof func('a'), expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
QUnit.module('lodash.keys');
|
QUnit.module('lodash.keys');
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
@@ -1489,88 +1533,43 @@
|
|||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
QUnit.module('lodash "Arrays" methods');
|
QUnit.module('lodash methods');
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
test('should allow a falsey `array` argument', function() {
|
test('should allow a falsey arguments', function() {
|
||||||
_.each([
|
var funcs = _.without.apply(_, [_.functions(_)].concat([
|
||||||
'compact',
|
'_iteratorTemplate',
|
||||||
'difference',
|
'_shimKeys',
|
||||||
'first',
|
'after',
|
||||||
'flatten',
|
'bind',
|
||||||
'groupBy',
|
'bindAll',
|
||||||
'indexOf',
|
'compose',
|
||||||
'initial',
|
'debounce',
|
||||||
'intersection',
|
'defer',
|
||||||
'last',
|
'delay',
|
||||||
'lastIndexOf',
|
'functions',
|
||||||
'max',
|
'memoize',
|
||||||
'min',
|
'once',
|
||||||
'range',
|
'partial',
|
||||||
'rest',
|
'tap',
|
||||||
'shuffle',
|
'template',
|
||||||
'sortBy',
|
'throttle',
|
||||||
'sortedIndex',
|
'wrap'
|
||||||
'union',
|
]));
|
||||||
'uniq',
|
|
||||||
'without',
|
_.each(funcs, function(methodName) {
|
||||||
'zip',
|
|
||||||
'zipObject'
|
|
||||||
], function(methodName) {
|
|
||||||
var func = _[methodName],
|
var func = _[methodName],
|
||||||
pass = true;
|
pass = true;
|
||||||
|
|
||||||
_.each(falsey, function(value, index) {
|
_.each(falsey, function(value, index) {
|
||||||
try {
|
try {
|
||||||
index ? func() : func(value);
|
index ? func(value) : func();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
pass = false;
|
pass = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ok(pass, methodName + ' allows a falsey `array` argument');
|
ok(pass, methodName + ' allows a falsey arguments');
|
||||||
});
|
|
||||||
});
|
|
||||||
}());
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
QUnit.module('lodash "Collections" methods');
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
test('should allow a falsey `collection` argument', function() {
|
|
||||||
_.each([
|
|
||||||
'contains',
|
|
||||||
'every',
|
|
||||||
'filter',
|
|
||||||
'find',
|
|
||||||
'forEach',
|
|
||||||
'invoke',
|
|
||||||
'map',
|
|
||||||
'pluck',
|
|
||||||
'reduce',
|
|
||||||
'reduceRight',
|
|
||||||
'reject',
|
|
||||||
'some',
|
|
||||||
'toArray'
|
|
||||||
], function(methodName) {
|
|
||||||
var func = _[methodName],
|
|
||||||
identity = _.identity,
|
|
||||||
pass = true;
|
|
||||||
|
|
||||||
_.each(falsey, function(value, index) {
|
|
||||||
try {
|
|
||||||
if (/^(?:contains|toArray)$/.test(methodName)) {
|
|
||||||
index ? func() : func(value);
|
|
||||||
} else if (index) {
|
|
||||||
func(value, identity);
|
|
||||||
}
|
|
||||||
} catch(e) {
|
|
||||||
pass = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ok(pass, methodName + ' allows a falsey `collection` argument');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}());
|
}());
|
||||||
|
|||||||
Reference in New Issue
Block a user