8000 Ensure isXYZ methods return boolean values and almost all methods all… · lodash/lodash@285f0bc · GitHub
[go: up one dir, main page]

Skip to content

Commit 285f0bc

Browse files
committed
Ensure isXYZ methods return boolean values and almost all methods allow falsey arguments.
Former-commit-id: a842eaf2fd262bed03df4a71b560b91801b7a75f
1 parent 07a370f commit 285f0bc

File tree

2 files changed

+147
-141
lines changed

2 files changed

+147
-141
lines changed

lodash.js

Lines changed: 78 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -379,15 +379,15 @@
379379
// iterate own properties using `Object.keys` if it's fast
380380
' <% if (isKeysFast && useHas) { %>\n' +
381381
' var ownIndex = -1,\n' +
382-
' ownProps = nativeKeys(iteratee),\n' +
382+
' ownProps = objectTypes[typeof iteratee] ? nativeKeys(iteratee) : [],\n' +
383383
' length = ownProps.length;\n\n' +
384384
' <%= objectBranch.beforeLoop %>;\n' +
385385
' while (++ownIndex < length) {\n' +
386386
' index = ownProps[ownIndex];\n' +
387-
' if (!(skipProto && index == \'prototype\')) {\n' +
388-
' value = iteratee[index];\n' +
389-
' <%= objectBranch.inLoop %>\n' +
390-
' }\n' +
387+
' <% if (!hasDontEnumBug) { %>if (!(skipProto && index == \'prototype\')) {\n <% } %>' +
388+
' value = iteratee[index];\n' +
389+
' <%= objectBranch.inLoop %>\n' +
390+
' <% if (!hasDontEnumBug) { %>}\n<% } %>' +
391391
' }' +
392392

393393
// else using a for-in loop
@@ -946,7 +946,6 @@
946946
*/
947947
var shimKeys = createIterator({
948948
'args': 'object',
949-
'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()',
950949
'init': '[]',
951950
'inLoop': 'result.push(index)'
952951
});
@@ -1231,7 +1230,7 @@
12311230
* // => true
12321231
*/
12331232
function has(object, property) {
1234-
return hasOwnProperty.call(object, property);
1233+
return object ? hasOwnProperty.call(object, property) : false;
12351234
}
12361235

12371236
/**
@@ -1282,7 +1281,7 @@
12821281
* // => true
12831282
*/
12841283
function isElement(value) {
1285-
return !!(value && value.nodeType == 1);
1284+
return value ? value.nodeType == 1 : false;
12861285
}
12871286

12881287
/**
@@ -1547,7 +1546,7 @@
15471546
// http://es5.github.com/#x8
15481547
// and avoid a V8 bug
15491548
// http://code.google.com/p/v8/issues/detail?id=2291
1550-
return value && objectTypes[typeof value];
1549+
return value ? objectTypes[typeof value] : false;
15511550
}
15521551

15531552
/**
@@ -1686,12 +1685,74 @@
16861685
* // => ['one', 'two', 'three'] (order is not guaranteed)
16871686
*/
16881687
var keys = !nativeKeys ? shimKeys : function(object) {
1688+
var type = typeof object;
1689+
16891690
// avoid iterating over the `prototype` property
1690-
return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype')
1691-
? shimKeys(object)
1692-
: nativeKeys(object);
1691+
if (type == 'function' && propertyIsEnumerable.call(object, 'prototype')) {
1692+
return shimKeys(object);
1693+
}
1694+
return object && objectTypes[type]
1695+
? nativeKeys(object)
1696+
: [];
16931697
};
16941698

1699+
/**
1700+
* Merges enumerable properties of the source object(s) into the `destination`
1701+
* object. Subsequent sources will overwrite propery assignments of previous
1702+
* sources.
1703+
*
1704+
* @static
1705+
* @memberOf _
1706+
* @category Objects
1707+
* @param {Object} object The destination object.
1708+
* @param {Object} [source1, source2, ...] The source objects.
1709+
* @param {Object} [indicator] Internally used to indicate that the `stack`
1710+
* argument is an array of traversed objects instead of another source object.
1711+
* @param {Array} [stack=[]] Internally used to keep track of traversed objects
1712+
* to avoid circular references.
1713+
* @returns {Object} Returns the destination object.
1714+
* @example
1715+
*
1716+
* var stooges = [
1717+
* { 'name': 'moe' },
1718+
* { 'name': 'larry' }
1719+
* ];
1720+
*
1721+
* var ages = [
1722+
* { 'age': 40 },
1723+
* { 'age': 50 }
1724+
* ];
1725+
*
1726+
* _.merge(stooges, ages);
1727+
* // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }]
1728+
*/
1729+
var merge = createIterator(extendIteratorOptions, {
1730+
'args': 'object, source, indicator, stack',
1731+
'top':
1732+
'var destValue, found, isArr, stackLength, recursive = indicator == isPlainObject;\n' +
1733+
'if (!recursive) stack = [];\n' +
1734+
'for (var argsIndex = 1, argsLength = recursive ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n' +
1735+
' if (iteratee = arguments[argsIndex]) {',
1736+
'inLoop':
1737+
'if (value && ((isArr = isArray(value)) || isPlainObject(value))) {\n' +
1738+
' found = false; stackLength = stack.length;\n' +
1739+
' while (stackLength--) {\n' +
1740+
' if (found = stack[stackLength].source == value) break\n' +
1741+
' }\n' +
1742+
' if (found) {\n' +
1743+
' result[index] = stack[stackLength].value\n' +
1744+
' } else {\n' +
1745+
' destValue = (destValue = result[index]) && isArr\n' +
1746+
' ? (isArray(destValue) ? destValue : [])\n' +
1747+
' : (isPlainObject(destValue) ? destValue : {});\n' +
1748+
' stack.push({ value: destValue, source: value });\n' +
1749+
' result[index] = callee(destValue, value, isPlainObject, stack)\n' +
1750+
' }\n' +
1751+
'} else if (value != null) {\n' +
1752+
' result[index] = value\n' +
1753+
'}'
1754+
});
1755+
16951756
/**
16961757
* Creates a shallow clone of `object` composed of the specified properties.
16971758
* Property names may be specified as individual arguments or as arrays of
@@ -1709,11 +1770,14 @@
17091770
* // => { 'name': 'moe', 'age': 40 }
17101771
*/
17111772
function pick(object) {
1773+
var result = {};
1774+
if (!object) {
1775+
return result;
1776+
}
17121777
var prop,
17131778
index = 0,
17141779
props = concat.apply(ArrayProto, arguments),
1715-
length = props.length,
1716-
result = {};
1780+
length = props.length;
17171781

17181782
// start `index` at `1` to skip `object`
17191783
while (++index < length) {
@@ -2025,63 +2089,6 @@
20252089
*/
20262090
var map = createIterator(baseIteratorOptions, mapIteratorOptions);
20272091

2028-
/**
2029-
* Merges enumerable properties of the source object(s) into the `destination`
2030-
* object. Subsequent sources will overwrite propery assignments of previous
2031-
* sources.
2032-
*
2033-
* @static
2034-
* @memberOf _
2035-
* @category Objects
2036-
* @param {Object} object The destination object.
2037-
* @param {Object} [source1, source2, ...] The source objects.
2038-
* @param {Object} [indicator] Internally used to indicate that the `stack`
2039-
* argument is an array of traversed objects instead of another source object.
2040-
* @param {Array} [stack=[]] Internally used to keep track of traversed objects
2041-
* to avoid circular references.
2042-
* @returns {Object} Returns the destination object.
2043-
* @example
2044-
*
2045-
* var stooges = [
2046-
* { 'name': 'moe' },
2047-
* { 'name': 'larry' }
2048-
* ];
2049-
*
2050-
* var ages = [
2051-
* { 'age': 40 },
2052-
* { 'age': 50 }
2053-
* ];
2054-
*
2055-
* _.merge(stooges, ages);
2056-
* // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }]
2057-
*/
2058-
var merge = createIterator(extendIteratorOptions, {
2059-
'args': 'object, source, indicator, stack',
2060-
'top':
2061-
'var destValue, found, isArr, stackLength, recursive = indicator == isPlainObject;\n' +
2062-
'if (!recursive) stack = [];\n' +
2063-
'for (var argsIndex = 1, argsLength = recursive ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n' +
2064-
' if (iteratee = arguments[argsIndex]) {',
2065-
'inLoop':
2066-
'if (value && ((isArr = isArray(value)) || isPlainObject(value))) {\n' +
2067-
' found = false; stackLength = stack.length;\n' +
2068-
' while (stackLength--) {\n' +
2069-
' if (found = stack[stackLength].source == value) break\n' +
2070-
' }\n' +
2071-
' if (found) {\n' +
2072-
' result[index] = stack[stackLength].value\n' +
2073-
' } else {\n' +
2074-
' destValue = (destValue = result[index]) && isArr\n' +
2075-
' ? (isArray(destValue) ? destValue : [])\n' +
2076-
' : (isPlainObject(destValue) ? destValue : {});\n' +
2077-
' stack.push({ value: destValue, source: value });\n' +
2078-
' result[index] = callee(destValue, value, isPlainObject, stack)\n' +
2079-
' }\n' +
2080-
'} else if (value != null) {\n' +
2081-
' result[index] = value\n' +
2082-
'}'
2083-
});
2084-
20852092
/**
20862093
* Retrieves the value of a specified property from all elements in
20872094
* the `collection`.

test/test.js

Lines changed: 69 additions & F438 70 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,50 @@
817817

818818
/*--------------------------------------------------------------------------*/
819819

820+
_.each([
821+
'isArguments',
822+
'isArray',
823+
'isBoolean',
824+
'isDate',
825+
'isElement',
826+
'isEmpty',
827+
'isEqual',
828+
'isFinite',
829+
'isFunction',
830+
'isNaN',
831+
'isNull',
832+
'isNumber',
833+
'isObject',
834+
'isRegExp',
835+
'isString',
836+
'isUndefined'
837+
], function(methodName) {
838+
var func = _[methodName];
839+
QUnit.module('lodash.' + methodName + ' result');
840+
841+
test('should return a boolean', function() {
842+
var expected = 'boolean';
843+
844+
equal(typeof func(arguments), expected);
845+
equal(typeof func([]), expected);
846+
equal(typeof func(true), expected);
847+
equal(typeof func(false), expected);
848+
equal(typeof func(new Date), expected);
849+
equal(typeof func(window.document && document.body), expected);
850+
equal(typeof func({}), expected);
851+
equal(typeof func(undefined), expected);
852+
equal(typeof func(Infinity), expected);
853+
equal(typeof func(_), expected);
854+
equal(typeof func(NaN), expected);
855+
equal(typeof func(null), expected);
856+
equal(typeof func(0), expected);
857+
equal(typeof func({ 'a': 1 }), expected);
858+
equal(typeof func('a'), expected);
859+
});
860+
});
861+
862+
/*--------------------------------------------------------------------------*/
863+
820864
QUnit.module('lodash.keys');
821865

822866
(function() {
@@ -1489,88 +1533,43 @@
14891533

14901534
/*--------------------------------------------------------------------------*/
14911535

1492-
QUnit.module('lodash "Arrays" methods');
1493-
1494-
(function() {
1495-
test('should allow a falsey `array` argument', function() {
1496-
_.each([
1497-
'compact',
1498-
'difference',
1499-
'first',
1500-
'flatten',
1501-
'groupBy',
1502-
'indexOf',
1503-
'initial',
1504-
'intersection',
1505-
'last',
1506-
'lastIndexOf',
1507-
'max',
1508-
'min',
1509-
'range',
1510-
'rest',
1511-
'shuffle',
1512-
'sortBy',
1513-
'sortedIndex',
1514-
'union',
1515-
'uniq',
1516-
'without',
1517-
'zip',
1518-
'zipObject'
1519-
], function(methodName) {
1520-
var func = _[methodName],
1521-
pass = true;
1522-
1523-
_.each(falsey, function(value, index) {
1524-
try {
1525-
index ? func() : func(value);
1526-
} catch(e) {
1527-
pass = false;
1528-
}
1529-
});
1530-
1531-
ok(pass, methodName + ' allows a falsey `array` argument');
1532-
});
1533-
});
1534-
}());
1535-
1536-
/*--------------------------------------------------------------------------*/
1537-
1538-
QUnit.module('lodash "Collections" methods');
1536+
QUnit.module('lodash methods');
15391537

15401538
(function() {
1541-
test('should allow a falsey `collection` argument', function() {
1542-
_.each([
1543-
'contains',
1544-
'every',
1545-
'filter',
1546-
'find',
1547-
'forEach',
1548-
'invoke',
1549-
'map',
1550-
'pluck',
1551-
'reduce',
1552-
'reduceRight',
1553-
'reject',
1554-
'some',
1555-
'toArray'
1556-
], function(methodName) {
1539+
test('should allow a falsey arguments', function() {
1540+
var funcs = _.without.apply(_, [_.functions(_)].concat([
1541+
'_iteratorTemplate',
1542+
'_shimKeys',
1543+
'after',
1544+
'bind',
1545+
'bindAll',
1546+
'compose',
1547+
'debounce',
1548+
'defer',
1549+
'delay',
1550+
'functions',
1551+
'memoize',
1552+
'once',
1553+
'partial',
1554+
'tap',
1555+
'template',
1556+
'throttle',
1557+
'wrap'
1558+
]));
1559+
1560+
_.each(funcs, function(methodName) {
15571561
var func = _[methodName],
1558-
identity = _.identity,
15591562
pass = true;
15601563

15611564
_.each(falsey, function(value, index) {
15621565
try {
1563-
if (/^(?:contains|toArray)$/.test(methodName)) {
1564-
index ? func() : func(value);
1565-
} else if (index) {
1566-
func(value, identity);
1567-
}
1566+
index ? func(value) : func();
15681567
} catch(e) {
15691568
pass = false;
15701569
}
15711570
});
15721571

1573-
ok(pass, methodName + ' allows a falsey `collection` argument');
1572+
ok(pass, methodName + ' allows a falsey arguments');
15741573
});
15751574
});
15761575
}());

0 commit comments

Comments
 (0)
0