Make a private each function to be used by _.forEach.

Former-commit-id: da9e22a66aef1ad9f4688f4fbb07e0806f8f0445
This commit is contained in:
John-David Dalton
2012-12-11 00:44:02 -08:00
parent 0b48b9c7d4
commit f14010a09d
2 changed files with 90 additions and 73 deletions

View File

@@ -73,7 +73,7 @@
'clone': ['assign', 'forEach', 'forOwn', 'isArray', 'isObject'],
'compact': [],
'compose': [],
'contains': ['forEach', 'indexOf', 'isString'],
'contains': ['indexOf', 'isString'],
'countBy': ['forEach'],
'debounce': [],
'defaults': ['isArguments'],
@@ -81,12 +81,12 @@
'delay': [],
'difference': ['indexOf'],
'escape': [],
'every': ['forEach', 'isArray'],
'filter': ['forEach', 'isArray'],
'every': ['isArray'],
'filter': ['isArray'],
'find': ['forEach'],
'first': [],
'flatten': ['isArray'],
'forEach': ['identity', 'isArguments', 'isString'],
'forEach': ['identity', 'isArguments', 'isArray', 'isString'],
'forIn': ['identity', 'isArguments'],
'forOwn': ['identity', 'isArguments'],
'functions': ['forIn', 'isFunction'],
@@ -95,7 +95,7 @@
'identity': [],
'indexOf': ['sortedIndex'],
'initial': [],
'intersection': ['filter', 'indexOf'],
'intersection': ['forEach', 'indexOf'],
'invert': ['forOwn'],
'invoke': ['forEach'],
'isArguments': [],
@@ -118,11 +118,11 @@
'keys': ['forOwn', 'isArguments', 'isObject'],
'last': [],
'lastIndexOf': [],
'map': ['forEach', 'isArray'],
'max': ['forEach', 'isArray', 'isString'],
'map': ['isArray'],
'max': ['isArray', 'isString'],
'memoize': [],
'merge': ['forOwn', 'isArray', 'isPlainObject'],
'min': ['forEach', 'isArray', 'isString'],
'min': ['isArray', 'isString'],
'mixin': ['forEach', 'forOwn', 'functions'],
'noConflict': [],
'object': [],
@@ -134,14 +134,14 @@
'pluck': ['map'],
'random': [],
'range': [],
'reduce': ['forEach', 'isArray'],
'reduce': ['isArray'],
'reduceRight': ['forEach', 'isString', 'keys'],
'reject': ['filter'],
'rest': [],
'result': ['isFunction'],
'shuffle': ['forEach'],
'size': ['keys'],
'some': ['forEach', 'isArray'],
'some': ['isArray'],
'sortBy': ['forEach'],
'sortedIndex': ['identity'],
'tap': ['mixin'],
@@ -368,10 +368,10 @@
});
// replace wrapper `Array` method assignments
source = source.replace(/^(?: *\/\/.*\n)*( *)forEach\(\['[\s\S]+?\n\1}$/m, function() {
source = source.replace(/^(?: *\/\/.*\n)*( *)each\(\['[\s\S]+?\n\1}$/m, function() {
return [
' // add `Array` mutator functions to the wrapper',
" forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {",
" each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {",
' var func = arrayRef[methodName];',
' lodash.prototype[methodName] = function() {',
' var value = this.__wrapped__;',
@@ -387,7 +387,7 @@
' });',
'',
' // add `Array` accessor functions to the wrapper',
" forEach(['concat', 'join', 'slice'], function(methodName) {",
" each(['concat', 'join', 'slice'], function(methodName) {",
' var func = arrayRef[methodName];',
' lodash.prototype[methodName] = function() {',
' var value = this.__wrapped__,',
@@ -1274,11 +1274,11 @@
dependencyMap.reduceRight = ['forEach', 'keys'];
}
if (isUnderscore) {
dependencyMap.contains = ['forEach', 'indexOf'];
dependencyMap.contains = ['indexOf'];
dependencyMap.isEqual = ['isArray', 'isFunction'];
dependencyMap.isEmpty = ['isArray', 'isString'];
dependencyMap.max = ['forEach', 'isArray'];
dependencyMap.min = ['forEach', 'isArray'];
dependencyMap.max = ['isArray'];
dependencyMap.min = ['isArray'];
dependencyMap.pick = [];
dependencyMap.template = ['defaults', 'escape'];
@@ -1390,7 +1390,7 @@
" if (typeof length == 'number') {",
' result = indexOf(collection, target) > -1;',
' } else {',
' forEach(collection, function(value) {',
' each(collection, function(value) {',
' return (result = value === target) && indicatorObject;',
' });',
' }',
@@ -1734,7 +1734,8 @@
if (isMobile) {
// inline all functions defined with `createIterator`
_.functions(lodash).forEach(function(methodName) {
var reFunc = RegExp('(\\bvar ' + methodName + ' *= *)createIterator\\(((?:{|[a-zA-Z])[\\s\\S]+?)\\);\\n');
// strip leading underscores to match pseudo private functions
var reFunc = RegExp('(\\bvar ' + methodName.replace(/^_/, '') + ' *= *)createIterator\\(((?:{|[a-zA-Z])[\\s\\S]+?)\\);\\n');
if (reFunc.test(source)) {
// extract, format, and inject the compiled function's source code
source = source.replace(reFunc, function(match, captured) {
@@ -1777,13 +1778,15 @@
});
}());
// remove chainability from `_.forEach`
source = source.replace(matchFunction(source, 'forEach'), function(match) {
return match.replace(/return result([};\s]+)$/, '$1');
// remove chainability from `each` and `_.forEach`
_.each(['each', 'forEach'], function(methodName) {
source = source.replace(matchFunction(source, methodName), function(match) {
return match.replace(/\n *return .+?([};\s]+)$/, '$1');
});
});
// unexpose "exit early" feature from `_.forEach`, `_.forIn`, and `_.forOwn`
_.each(['forEach', 'forIn', 'forOwn'], function(methodName) {
// unexpose "exit early" feature of `each`, `_.forEach`, `_.forIn`, and `_.forOwn`
_.each(['each', 'forEach', 'forIn', 'forOwn'], function(methodName) {
source = source.replace(matchFunction(source, methodName), function(match) {
return match.replace(/=== *false\)/g, '=== indicatorObject)');
});
@@ -1962,7 +1965,7 @@
// remove all `lodash.prototype` additions
source = source
.replace(/(?:\s*\/\/.*)*\n( *)forOwn\(lodash, *function\(func, *methodName\)[\s\S]+?\n\1}.+/g, '')
.replace(/(?:\s*\/\/.*)*\n( *)forEach\(\['[\s\S]+?\n\1}.+/g, '')
.replace(/(?:\s*\/\/.*)*\n( *)each\(\['[\s\S]+?\n\1}.+/g, '')
.replace(/(?:\s*\/\/.*)*\s*lodash\.prototype.+\n/g, '')
.replace(/(?:\s*\/\/.*)*\s*mixin\(lodash\).+\n/, '');
}

112
lodash.js
View File

@@ -116,8 +116,7 @@
stringClass = '[object String]';
/** Detect various environments */
var isFirefox = !/1/.test(Function('1')),
isIeOpera = !!window.attachEvent,
var isIeOpera = !!window.attachEvent,
isV8 = nativeBind && !/\n|true/.test(nativeBind + isIeOpera);
/* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */
@@ -247,24 +246,25 @@
* method chaining.
*
* The chainable wrapper functions are:
* `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
* `compose`, `countBy`, `debounce`, `defaults`, `defer`, `delay`, `difference`,
* `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, `compose`,
* `concat`, `countBy`, `debounce`, `defaults`, `defer`, `delay`, `difference`,
* `filter`, `flatten`, `forEach`, `forIn`, `forOwn`, `functions`, `groupBy`,
* `initial`, `intersection`, `invert`, `invoke`, `keys`, `map`, `max`, `memoize`,
* `merge`, `min`, `object`, `omit`, `once`, `pairs`, `partial`, `pick`, `pluck`,
* `range`, `reject`, `rest`, `shuffle`, `sortBy`, `tap`, `throttle`, `times`,
* `toArray`, `union`, `uniq`, `values`, `where`, `without`, `wrap`, and `zip`
* `push`, `range`, `reject`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
* `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `union`, `uniq`,
* `unshift`, `values`, `where`, `without`, `wrap`, and `zip`
*
* The non-chainable wrapper functions are:
* `clone`, `contains`, `escape`, `every`, `find`, `has`, `identity`, `indexOf`,
* `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`,
* `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, `isObject`,
* `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `lastIndexOf`, `mixin`,
* `noConflict`, `random`, `reduce`, `reduceRight`, `result`, `size`, `some`,
* `sortedIndex`, `template`, `unescape`, and `uniqueId`
* `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, `lastIndexOf`,
* `mixin`, `noConflict`, `pop`, `random`, `reduce`, `reduceRight`, `result`,
* `shift`, `size`, `some`, `sortedIndex`, `template`, `unescape`, and `uniqueId`
*
* The wrapper functions `first` and `last` return wrapped values when `n` is
* passed, otherwise return unwrapped values.
* passed, otherwise they return unwrapped values.
*
* @name _
* @constructor
@@ -334,25 +334,6 @@
/*--------------------------------------------------------------------------*/
/**
* Creates a function from the given `args` and `body` strings.
*
* @private
* @param {String} args The comma separated function arguments.
* @param {String} body The function body.
* @returns {Function} The new function.
*/
function createFunction(args, body) {
// the newline, in `'\n}'`, is required to avoid errors if `body` ends
// with a single line comment
return window.eval('(function(' + args + ') {' + body + '\n})');
}
// use `eval` to avoid Firefox's unoptimized `Function` constructor
// http://bugzil.la/804933
if (isIeOpera || isV8 || !isFirefox) {
createFunction = Function;
}
/**
* The template used to create iterator functions.
*
@@ -476,9 +457,9 @@
};
/**
* Reusable iterator options shared by `forEach`, `forIn`, and `forOwn`.
* Reusable iterator options shared by `each`, `forIn`, and `forOwn`.
*/
var forEachIteratorOptions = {
var eachIteratorOptions = {
'args': 'collection, callback, thisArg',
'top': "callback = callback && typeof thisArg == 'undefined' ? callback : createCallback(callback, thisArg)",
'arrayLoop': 'if (callback(iteratee[index], index, collection) === false) return result',
@@ -696,7 +677,7 @@
data.firstArg = /^[^,]+/.exec(args)[0];
// create the function factory
var factory = createFunction(
var factory = Function(
'createCallback, hasOwnProperty, isArguments, isString, objectTypes, ' +
'nativeKeys, propertyIsEnumerable',
'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
@@ -708,6 +689,21 @@
);
}
/**
* A function compiled to iterate `arguments` objects, arrays, objects, and
* strings consistenly across environments, executing the `callback` for each
* element in the `collection`. The `callback` is bound to `thisArg` and invoked
* with three arguments; (value, index|key, collection). Callbacks may exit
* iteration early by explicitly returning `false`.
*
* @private
* @param {Array|Object|String} collection The collection to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Array|Object|String} Returns `collection`.
*/
var each = createIterator(eachIteratorOptions);
/**
* Used by `template` to escape characters for inclusion in compiled
* string literals.
@@ -867,7 +863,7 @@
* });
* // => alerts 'name' and 'bark' (order is not guaranteed)
*/
var forIn = createIterator(forEachIteratorOptions, forOwnIteratorOptions, {
var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, {
'useHas': false
});
@@ -891,7 +887,7 @@
* });
* // => alerts '0', '1', and 'length' (order is not guaranteed)
*/
var forOwn = createIterator(forEachIteratorOptions, forOwnIteratorOptions);
var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions);
/**
* A fallback implementation of `isPlainObject` that checks if a given `value`
@@ -1941,7 +1937,7 @@
: indexOf(collection, target, fromIndex)
) > -1;
} else {
forEach(collection, function(value) {
each(collection, function(value) {
if (++index >= fromIndex) {
return !(result = value === target);
}
@@ -2020,7 +2016,7 @@
}
}
} else {
forEach(collection, function(value, index, collection) {
each(collection, function(value, index, collection) {
return (result = !!callback(value, index, collection));
});
}
@@ -2060,7 +2056,7 @@
}
}
} else {
forEach(collection, function(value, index, collection) {
each(collection, function(value, index, collection) {
if (callback(value, index, collection)) {
result.push(value);
}
@@ -2124,7 +2120,24 @@
* _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert);
* // => alerts each number value (order is not guaranteed)
*/
var forEach = createIterator(forEachIteratorOptions);
function forEach(collection, callback, thisArg) {
if (isArray(collection)) {
var index = -1,
length = collection.length;
if (!callback || typeof thisArg != 'undefined') {
callback = createCallback(callback, thisArg);
}
while (++index < length) {
if (callback(collection[index], index, collection) === false) {
break;
}
}
} else {
each(collection, callback, thisArg);
}
return collection;
}
/**
* Creates an object composed of keys returned from running each element of
@@ -2228,7 +2241,7 @@
result[index] = callback(collection[index], index, collection);
}
} else {
forEach(collection, function(value, key, collection) {
each(collection, function(value, key, collection) {
result[++index] = callback(value, key, collection);
});
}
@@ -2270,7 +2283,7 @@
? charAtCallback
: createCallback(callback, thisArg);
forEach(collection, function(value, index, collection) {
each(collection, function(value, index, collection) {
var current = callback(value, index, collection);
if (current > computed) {
computed = current;
@@ -2316,7 +2329,7 @@
? charAtCallback
: createCallback(callback, thisArg);
forEach(collection, function(value, index, collection) {
each(collection, function(value, index, collection) {
var current = callback(value, index, collection);
if (current < computed) {
computed = current;
@@ -2393,7 +2406,7 @@
accumulator = callback(accumulator, collection[index], index, collection);
}
} else {
forEach(collection, function(value, index, collection) {
each(collection, function(value, index, collection) {
accumulator = noaccum
? (noaccum = false, value)
: callback(accumulator, value, index, collection)
@@ -2550,7 +2563,7 @@
}
}
} else {
forEach(collection, function(value, index, collection) {
each(collection, function(value, index, collection) {
return !(result = callback(value, index, collection));
});
}
@@ -4006,7 +4019,7 @@
: '';
try {
result = createFunction('_', 'return ' + source + sourceURL)(lodash);
result = Function('_', 'return ' + source + sourceURL)(lodash);
} catch(e) {
e.source = source;
throw e;
@@ -4136,7 +4149,7 @@
* // => '1,2,3'
*/
function wrapperToString() {
return String(this.__wrapped__);
return this.__wrapped__ + '';
}
/**
@@ -4323,7 +4336,7 @@
lodash.prototype.valueOf = wrapperValueOf;
// add `Array` functions that return unwrapped values
forEach(['join', 'pop', 'shift'], function(methodName) {
each(['join', 'pop', 'shift'], function(methodName) {
var func = arrayRef[methodName];
lodash.prototype[methodName] = function() {
return func.apply(this.__wrapped__, arguments);
@@ -4331,7 +4344,7 @@
});
// add `Array` functions that return the wrapped value
forEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
each(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
var func = arrayRef[methodName];
lodash.prototype[methodName] = function() {
func.apply(this.__wrapped__, arguments);
@@ -4340,7 +4353,7 @@
});
// add `Array` functions that return new wrapped values
forEach(['concat', 'slice', 'splice'], function(methodName) {
each(['concat', 'slice', 'splice'], function(methodName) {
var func = arrayRef[methodName];
lodash.prototype[methodName] = function() {
var result = func.apply(this.__wrapped__, arguments);
@@ -4351,7 +4364,7 @@
// avoid array-like object bugs with `Array#shift` and `Array#splice`
// in Firefox < 10 and IE < 9
if (hasObjectSpliceBug) {
forEach(['shift', 'splice'], function(methodName) {
each(['shift', 'splice'], function(methodName) {
var func = lodash.prototype[methodName];
lodash.prototype[methodName] = function() {
var value = this.__wrapped__,
@@ -4366,6 +4379,7 @@
}
// add pseudo private property to be used and removed during the build process
lodash._each = each;
lodash._iteratorTemplate = iteratorTemplate;
/*--------------------------------------------------------------------------*/