mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-02 08:07:50 +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
|
||||
' <% if (isKeysFast && useHas) { %>\n' +
|
||||
' var ownIndex = -1,\n' +
|
||||
' ownProps = nativeKeys(iteratee),\n' +
|
||||
' ownProps = objectTypes[typeof iteratee] ? nativeKeys(iteratee) : [],\n' +
|
||||
' length = ownProps.length;\n\n' +
|
||||
' <%= objectBranch.beforeLoop %>;\n' +
|
||||
' while (++ownIndex < length) {\n' +
|
||||
' index = ownProps[ownIndex];\n' +
|
||||
' if (!(skipProto && index == \'prototype\')) {\n' +
|
||||
' value = iteratee[index];\n' +
|
||||
' <%= objectBranch.inLoop %>\n' +
|
||||
' }\n' +
|
||||
' <% if (!hasDontEnumBug) { %>if (!(skipProto && index == \'prototype\')) {\n <% } %>' +
|
||||
' value = iteratee[index];\n' +
|
||||
' <%= objectBranch.inLoop %>\n' +
|
||||
' <% if (!hasDontEnumBug) { %>}\n<% } %>' +
|
||||
' }' +
|
||||
|
||||
// else using a for-in loop
|
||||
@@ -946,7 +946,6 @@
|
||||
*/
|
||||
var shimKeys = createIterator({
|
||||
'args': 'object',
|
||||
'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()',
|
||||
'init': '[]',
|
||||
'inLoop': 'result.push(index)'
|
||||
});
|
||||
@@ -1231,7 +1230,7 @@
|
||||
* // => true
|
||||
*/
|
||||
function has(object, property) {
|
||||
return hasOwnProperty.call(object, property);
|
||||
return object ? hasOwnProperty.call(object, property) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1282,7 +1281,7 @@
|
||||
* // => true
|
||||
*/
|
||||
function isElement(value) {
|
||||
return !!(value && value.nodeType == 1);
|
||||
return value ? value.nodeType == 1 : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1547,7 +1546,7 @@
|
||||
// http://es5.github.com/#x8
|
||||
// and avoid a V8 bug
|
||||
// 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)
|
||||
*/
|
||||
var keys = !nativeKeys ? shimKeys : function(object) {
|
||||
var type = typeof object;
|
||||
|
||||
// avoid iterating over the `prototype` property
|
||||
return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype')
|
||||
? shimKeys(object)
|
||||
: nativeKeys(object);
|
||||
if (type == 'function' && propertyIsEnumerable.call(object, 'prototype')) {
|
||||
return shimKeys(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.
|
||||
* Property names may be specified as individual arguments or as arrays of
|
||||
@@ -1709,11 +1770,14 @@
|
||||
* // => { 'name': 'moe', 'age': 40 }
|
||||
*/
|
||||
function pick(object) {
|
||||
var result = {};
|
||||
if (!object) {
|
||||
return result;
|
||||
}
|
||||
var prop,
|
||||
index = 0,
|
||||
props = concat.apply(ArrayProto, arguments),
|
||||
length = props.length,
|
||||
result = {};
|
||||
length = props.length;
|
||||
|
||||
// start `index` at `1` to skip `object`
|
||||
while (++index < length) {
|
||||
@@ -2025,63 +2089,6 @@
|
||||
*/
|
||||
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
|
||||
* 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');
|
||||
|
||||
(function() {
|
||||
@@ -1489,88 +1533,43 @@
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
QUnit.module('lodash "Arrays" methods');
|
||||
QUnit.module('lodash methods');
|
||||
|
||||
(function() {
|
||||
test('should allow a falsey `array` argument', function() {
|
||||
_.each([
|
||||
'compact',
|
||||
'difference',
|
||||
'first',
|
||||
'flatten',
|
||||
'groupBy',
|
||||
'indexOf',
|
||||
'initial',
|
||||
'intersection',
|
||||
'last',
|
||||
'lastIndexOf',
|
||||
'max',
|
||||
'min',
|
||||
'range',
|
||||
'rest',
|
||||
'shuffle',
|
||||
'sortBy',
|
||||
'sortedIndex',
|
||||
'union',
|
||||
'uniq',
|
||||
'without',
|
||||
'zip',
|
||||
'zipObject'
|
||||
], function(methodName) {
|
||||
test('should allow a falsey arguments', function() {
|
||||
var funcs = _.without.apply(_, [_.functions(_)].concat([
|
||||
'_iteratorTemplate',
|
||||
'_shimKeys',
|
||||
'after',
|
||||
'bind',
|
||||
'bindAll',
|
||||
'compose',
|
||||
'debounce',
|
||||
'defer',
|
||||
'delay',
|
||||
'functions',
|
||||
'memoize',
|
||||
'once',
|
||||
'partial',
|
||||
'tap',
|
||||
'template',
|
||||
'throttle',
|
||||
'wrap'
|
||||
]));
|
||||
|
||||
_.each(funcs, function(methodName) {
|
||||
var func = _[methodName],
|
||||
pass = true;
|
||||
|
||||
_.each(falsey, function(value, index) {
|
||||
try {
|
||||
index ? func() : func(value);
|
||||
index ? func(value) : func();
|
||||
} catch(e) {
|
||||
pass = false;
|
||||
}
|
||||
});
|
||||
|
||||
ok(pass, methodName + ' allows a falsey `array` argument');
|
||||
});
|
||||
});
|
||||
}());
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
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');
|
||||
ok(pass, methodName + ' allows a falsey arguments');
|
||||
});
|
||||
});
|
||||
}());
|
||||
|
||||
Reference in New Issue
Block a user