Add array and object pools to lodash.

Former-commit-id: f038284d6a544e146dc271ed0fbea0d7401593d4
This commit is contained in:
John-David Dalton
2013-06-02 21:25:57 -07:00
parent 9e63270fc5
commit 13ead0085d
3 changed files with 336 additions and 122 deletions

122
build.js
View File

@@ -85,7 +85,7 @@
'bind': ['createBound'], 'bind': ['createBound'],
'bindAll': ['bind', 'functions'], 'bindAll': ['bind', 'functions'],
'bindKey': ['createBound'], 'bindKey': ['createBound'],
'clone': ['assign', 'forEach', 'forOwn', 'isArray', 'isObject', 'isNode', 'slice'], 'clone': ['assign', 'forEach', 'forOwn', 'getArray', 'isArray', 'isObject', 'isNode', 'releaseArray', 'slice'],
'cloneDeep': ['clone'], 'cloneDeep': ['clone'],
'compact': [], 'compact': [],
'compose': [], 'compose': [],
@@ -114,7 +114,7 @@
'identity': [], 'identity': [],
'indexOf': ['basicIndexOf', 'sortedIndex'], 'indexOf': ['basicIndexOf', 'sortedIndex'],
'initial': ['slice'], 'initial': ['slice'],
'intersection': ['createCache'], 'intersection': ['createCache', 'getArray', 'releaseArray'],
'invert': ['keys'], 'invert': ['keys'],
'invoke': ['forEach'], 'invoke': ['forEach'],
'isArguments': [], 'isArguments': [],
@@ -123,7 +123,7 @@
'isDate': [], 'isDate': [],
'isElement': [], 'isElement': [],
'isEmpty': ['forOwn', 'isArguments', 'isFunction'], 'isEmpty': ['forOwn', 'isArguments', 'isFunction'],
'isEqual': ['forIn', 'isArguments', 'isFunction', 'isNode'], 'isEqual': ['forIn', 'getArray', 'isArguments', 'isFunction', 'isNode', 'releaseArray'],
'isFinite': [], 'isFinite': [],
'isFunction': [], 'isFunction': [],
'isNaN': ['isNumber'], 'isNaN': ['isNumber'],
@@ -140,7 +140,7 @@
'map': ['basicEach', 'createCallback', 'isArray'], 'map': ['basicEach', 'createCallback', 'isArray'],
'max': ['basicEach', 'charAtCallback', 'createCallback', 'isArray', 'isString'], 'max': ['basicEach', 'charAtCallback', 'createCallback', 'isArray', 'isString'],
'memoize': [], 'memoize': [],
'merge': ['forEach', 'forOwn', 'isArray', 'isObject', 'isPlainObject'], 'merge': ['forEach', 'forOwn', 'getArray', 'isArray', 'isObject', 'isPlainObject', 'releaseArray'],
'min': ['basicEach', 'charAtCallback', 'createCallback', 'isArray', 'isString'], 'min': ['basicEach', 'charAtCallback', 'createCallback', 'isArray', 'isString'],
'mixin': ['forEach', 'functions'], 'mixin': ['forEach', 'functions'],
'noConflict': [], 'noConflict': [],
@@ -163,7 +163,7 @@
'shuffle': ['forEach'], 'shuffle': ['forEach'],
'size': ['keys'], 'size': ['keys'],
'some': ['basicEach', 'createCallback', 'isArray'], 'some': ['basicEach', 'createCallback', 'isArray'],
'sortBy': ['compareAscending', 'createCallback', 'forEach'], 'sortBy': ['compareAscending', 'createCallback', 'forEach', 'getObject', 'releaseObject'],
'sortedIndex': ['createCallback', 'identity'], 'sortedIndex': ['createCallback', 'identity'],
'tap': ['value'], 'tap': ['value'],
'template': ['defaults', 'escape', 'escapeStringChar', 'keys', 'values'], 'template': ['defaults', 'escape', 'escapeStringChar', 'keys', 'values'],
@@ -173,7 +173,7 @@
'transform': ['createCallback', 'createObject', 'forOwn', 'isArray'], 'transform': ['createCallback', 'createObject', 'forOwn', 'isArray'],
'unescape': ['unescapeHtmlChar'], 'unescape': ['unescapeHtmlChar'],
'union': ['isArray', 'uniq'], 'union': ['isArray', 'uniq'],
'uniq': ['createCache', 'getIndexOf', 'overloadWrapper'], 'uniq': ['createCache', 'getArray', 'getIndexOf', 'overloadWrapper', 'releaseArray'],
'uniqueId': [], 'uniqueId': [],
'unzip': ['max', 'pluck'], 'unzip': ['max', 'pluck'],
'value': ['basicEach', 'forOwn', 'isArray', 'lodashWrapper'], 'value': ['basicEach', 'forOwn', 'isArray', 'lodashWrapper'],
@@ -190,17 +190,21 @@
'charAtCallback': [], 'charAtCallback': [],
'compareAscending': [], 'compareAscending': [],
'createBound': ['createObject', 'isFunction', 'isObject'], 'createBound': ['createObject', 'isFunction', 'isObject'],
'createCache': ['basicIndexOf', 'getIndexOf'], 'createCache': ['basicIndexOf', 'getArray', 'getIndexOf', 'getObject', 'releaseObject'],
'createIterator': ['iteratorTemplate'], 'createIterator': ['getObject', 'iteratorTemplate', 'releaseObject'],
'createObject': [ 'isObject', 'noop'], 'createObject': [ 'isObject', 'noop'],
'escapeHtmlChar': [], 'escapeHtmlChar': [],
'escapeStringChar': [], 'escapeStringChar': [],
'getArray': [],
'getIndexOf': ['basicIndexOf', 'indexOf'], 'getIndexOf': ['basicIndexOf', 'indexOf'],
'getObject': [],
'iteratorTemplate': [], 'iteratorTemplate': [],
'isNode': [], 'isNode': [],
'lodashWrapper': [], 'lodashWrapper': [],
'noop': [], 'noop': [],
'overloadWrapper': ['createCallback'], 'overloadWrapper': ['createCallback'],
'releaseArray': [],
'releaseObject': [],
'shimIsPlainObject': ['forIn', 'isArguments', 'isFunction', 'isNode'], 'shimIsPlainObject': ['forIn', 'isArguments', 'isFunction', 'isNode'],
'shimKeys': ['createIterator', 'isArguments'], 'shimKeys': ['createIterator', 'isArguments'],
'slice': [], 'slice': [],
@@ -222,9 +226,7 @@
'shadowedProps', 'shadowedProps',
'top', 'top',
'useHas', 'useHas',
'useKeys', 'useKeys'
'shimIsPlainObject',
'shimKyes'
]; ];
/** List of all methods */ /** List of all methods */
@@ -309,9 +311,6 @@
'unzip' 'unzip'
]; ];
/** List of Underscore methods */
var underscoreMethods = _.without.apply(_, [allMethods].concat(lodashOnlyMethods));
/** List of ways to export the `lodash` function */ /** List of ways to export the `lodash` function */
var exportsAll = [ var exportsAll = [
'amd', 'amd',
@@ -341,15 +340,22 @@
'createIterator', 'createIterator',
'escapeHtmlChar', 'escapeHtmlChar',
'escapeStringChar', 'escapeStringChar',
'getArray',
'getObject',
'isNode', 'isNode',
'iteratorTemplate', 'iteratorTemplate',
'lodashWrapper', 'lodashWrapper',
'overloadWrapper', 'overloadWrapper',
'releaseArray',
'releaseObject',
'shimIsPlainObject', 'shimIsPlainObject',
'shimKeys', 'shimKeys',
'slice', 'slice',
'unescapeHtmlChar' 'unescapeHtmlChar'
] ];
/** List of Underscore methods */
var underscoreMethods = _.without.apply(_, [allMethods].concat(lodashOnlyMethods, privateMethods));
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
@@ -990,7 +996,7 @@
// match a function declaration // match a function declaration
'function ' + funcName + '\\b[\\s\\S]+?\\n\\1}|' + 'function ' + funcName + '\\b[\\s\\S]+?\\n\\1}|' +
// match a variable declaration with function expression // match a variable declaration with function expression
'var ' + funcName + ' *=.*?function[\\s\\S]+?\\n\\1};' + 'var ' + funcName + ' *=.*?function[\\s\\S]+?\\n\\1}(?:\\(\\)\\))?;' +
// end non-capturing group // end non-capturing group
')\\n' ')\\n'
))); )));
@@ -1051,24 +1057,30 @@
return source; return source;
} }
// remove data object property assignment // remove data object property assignment
var modified = snippet var modified = snippet.replace(RegExp("^(?: *\\/\\/.*\\n)* *data\\." + identifier + " *= *(.+\\n+)", 'm'), function(match, postlude) {
.replace(RegExp("^(?: *\\/\\/.*\\n)* *'" + identifier + "':.+\\n+", 'm'), '') return /\bdata\b/.test(postlude) ? postlude : '';
.replace(/,(?=\s*})/, ''); });
source = source.replace(snippet, function() { source = source.replace(snippet, function() {
return modified; return modified;
}); });
// clip at the `factory` assignment // clip to the `factory` assignment
snippet = modified.match(/Function\([\s\S]+$/)[0]; snippet = modified.match(/Function\([\s\S]+$/)[0];
modified = snippet // remove `factory` arguments
.replace(RegExp('\\b' + identifier + '\\b,? *', 'g'), '') source = source.replace(snippet, function(match) {
.replace(/, *(?=',)/, '') return match
.replace(/,(?=\s*\))/, '') .replace(RegExp('\\b' + identifier + '\\b,? *', 'g'), '')
.replace(/, *(?=',)/, '')
.replace(/,(?=\s*\))/, '');
});
source = source.replace(snippet, function() { // remove property assignment from `getObject`
return modified; source = source.replace(matchFunction(source, 'getObject'), function(match) {
return match
.replace(RegExp("^(?: *\\/\\/.*\\n)* *'" + identifier + "':.+\\n+", 'm'), '')
.replace(/,(?=\s*})/, '');
}); });
return source; return source;
@@ -1952,6 +1964,15 @@
dependencyMap.where.push('find', 'isEmpty'); dependencyMap.where.push('find', 'isEmpty');
} }
_.each(['clone', 'difference', 'intersection', 'isEqual', 'sortBy', 'uniq'], function(methodName) {
if (methodName == 'clone'
? (!useLodashMethod('clone') && !useLodashMethod('cloneDeep'))
: !useLodashMethod(methodName)
) {
dependencyMap[methodName] = _.without(dependencyMap[methodName], 'getArray', 'getObject', 'releaseArray', 'releaseObject');
}
});
_.each(['debounce', 'throttle'], function(methodName) { _.each(['debounce', 'throttle'], function(methodName) {
if (!useLodashMethod(methodName)) { if (!useLodashMethod(methodName)) {
dependencyMap[methodName] = []; dependencyMap[methodName] = [];
@@ -1964,6 +1985,12 @@
} }
}); });
_.each(['flatten', 'uniq'], function(methodName) {
if (!useLodashMethod(methodName)) {
dependencyMap[methodName] = _.without(dependencyMap[methodName], 'overloadWrapper');
}
});
_.each(['max', 'min'], function(methodName) { _.each(['max', 'min'], function(methodName) {
if (!useLodashMethod(methodName)) { if (!useLodashMethod(methodName)) {
dependencyMap[methodName] = _.without(dependencyMap[methodName], 'charAtCallback', 'isArray', 'isString'); dependencyMap[methodName] = _.without(dependencyMap[methodName], 'charAtCallback', 'isArray', 'isString');
@@ -1973,6 +2000,12 @@
if (isModern || isUnderscore) { if (isModern || isUnderscore) {
dependencyMap.reduceRight = _.without(dependencyMap.reduceRight, 'isString'); dependencyMap.reduceRight = _.without(dependencyMap.reduceRight, 'isString');
_.each(['assign', 'basicEach', 'defaults', 'forIn', 'forOwn', 'shimKeys'], function(methodName) {
if (!(isUnderscore && useLodashMethod(methodName))) {
dependencyMap[methodName] = _.without(dependencyMap[methodName], 'createIterator');
}
});
_.each(['at', 'forEach', 'toArray'], function(methodName) { _.each(['at', 'forEach', 'toArray'], function(methodName) {
if (!(isUnderscore && useLodashMethod(methodName))) { if (!(isUnderscore && useLodashMethod(methodName))) {
dependencyMap[methodName] = _.without(dependencyMap[methodName], 'isString'); dependencyMap[methodName] = _.without(dependencyMap[methodName], 'isString');
@@ -2644,6 +2677,32 @@
'}' '}'
].join('\n')); ].join('\n'));
} }
// replace `_.sortBy`
if (!useLodashMethod('sortBy')) {
source = replaceFunction(source, 'sortBy', [
'function sortBy(collection, callback, thisArg) {',
' var index = -1,',
' length = collection ? collection.length : 0,',
" result = Array(typeof length == 'number' ? length : 0);",
'',
' callback = lodash.createCallback(callback, thisArg);',
' forEach(collection, function(value, key, collection) {',
' result[++index] = {',
" 'criteria': callback(value, key, collection),",
" 'index': index,",
" 'value': value",
' };',
' });',
'',
' length = result.length;',
' result.sort(compareAscending);',
' while (length--) {',
' result[length] = result[length].value;',
' }',
' return result;',
'}'
].join('\n'));
}
// replace `_.template` // replace `_.template`
if (!useLodashMethod('template')) { if (!useLodashMethod('template')) {
// remove `_.templateSettings.imports assignment // remove `_.templateSettings.imports assignment
@@ -2846,7 +2905,10 @@
// replace `slice` with `nativeSlice.call` // replace `slice` with `nativeSlice.call`
_.each(['clone', 'first', 'initial', 'last', 'rest', 'toArray'], function(methodName) { _.each(['clone', 'first', 'initial', 'last', 'rest', 'toArray'], function(methodName) {
if (!useLodashMethod(methodName)) { if (methodName == 'clone'
? (!useLodashMethod('clone') && !useLodashMethod('cloneDeep'))
: !useLodashMethod(methodName)
) {
source = source.replace(matchFunction(source, methodName), function(match) { source = source.replace(matchFunction(source, methodName), function(match) {
return match.replace(/([^.])\bslice\(/g, '$1nativeSlice.call('); return match.replace(/([^.])\bslice\(/g, '$1nativeSlice.call(');
}); });
@@ -3163,6 +3225,12 @@
source = removeVar(source, 'htmlEscapes'); source = removeVar(source, 'htmlEscapes');
source = removeVar(source, 'htmlUnescapes'); source = removeVar(source, 'htmlUnescapes');
} }
if (isRemoved(source, 'getArray', 'releaseArray')) {
source = removeVar(source, 'arrayPool');
}
if (isRemoved(source, 'getObject', 'releaseObject')) {
source = removeVar(source, 'objectPool');
}
if (isRemoved(source, 'invert')) { if (isRemoved(source, 'invert')) {
source = replaceVar(source, 'htmlUnescapes', "{'&amp;':'&','&lt;':'<','&gt;':'>','&quot;':'\"','&#x27;':\"'\"}"); source = replaceVar(source, 'htmlUnescapes', "{'&amp;':'&','&lt;':'<','&gt;':'>','&quot;':'\"','&#x27;':\"'\"}");
} }

View File

@@ -312,11 +312,32 @@
// remove debug sourceURL use in `_.template` // remove debug sourceURL use in `_.template`
source = source.replace(/(?:\s*\/\/.*\n)* *var sourceURL[^;]+;|\+ *sourceURL/g, ''); source = source.replace(/(?:\s*\/\/.*\n)* *var sourceURL[^;]+;|\+ *sourceURL/g, '');
// minify internal properties used by 'compareAscending' and `_.sortBy` // minify internal properties
(function() { (function() {
var properties = ['criteria', 'index', 'value'], var methods = [
snippets = source.match(/( +)function (?:compareAscending|sortBy)\b[\s\S]+?\n\1}/g); 'compareAscending',
'createCache',
'difference',
'getObject',
'intersection',
'releaseObject',
'sortBy',
'uniq'
];
var props = [
'array',
'cache',
'contains',
'criteria',
'index',
'indexOf',
'initArray',
'release',
'value'
];
var snippets = source.match(RegExp('^( +)(?:var|function) +(?:' + methods.join('|') + ')\\b[\\s\\S]+?\\n\\1}', 'gm'));
if (!snippets) { if (!snippets) {
return; return;
} }
@@ -324,11 +345,12 @@
var modified = snippet; var modified = snippet;
// minify properties // minify properties
properties.forEach(function(property, index) { props.forEach(function(prop, index) {
var minName = minNames[index], // use minified names different than those chosen for `iteratorOptions`
reBracketProp = RegExp("\\['(" + property + ")'\\]", 'g'), var minName = minNames[iteratorOptions.length + index],
reDotProp = RegExp('\\.' + property + '\\b', 'g'), reBracketProp = RegExp("\\['(" + prop + ")'\\]", 'g'),
rePropColon = RegExp("([^?\\s])\\s*([\"'])?\\b" + property + "\\2 *:", 'g'); reDotProp = RegExp('\\.' + prop + '\\b', 'g'),
rePropColon = RegExp("([^?\\s])\\s*([\"'])?\\b" + prop + "\\2 *:", 'g');
modified = modified modified = modified
.replace(reBracketProp, "['" + minName + "']") .replace(reBracketProp, "['" + minName + "']")
@@ -352,8 +374,8 @@
'createIterator\\((?:{|[a-zA-Z]+)[\\s\\S]*?\\);\\n', 'createIterator\\((?:{|[a-zA-Z]+)[\\s\\S]*?\\);\\n',
// match variables storing `createIterator` options // match variables storing `createIterator` options
'( +)var [a-zA-Z]+IteratorOptions\\b[\\s\\S]+?\\n\\2}', '( +)var [a-zA-Z]+IteratorOptions\\b[\\s\\S]+?\\n\\2}',
// match the the `createIterator` function // match the `createIterator`, `getObject`, and `releaseObject` functions
'( +)function createIterator\\b[\\s\\S]+?\\n\\3}' '( +)function (?:createIterator|getObject|releaseObject)\\b[\\s\\S]+?\\n\\3}'
].join('|'), 'g') ].join('|'), 'g')
); );
@@ -363,7 +385,7 @@
} }
snippets.forEach(function(snippet, index) { snippets.forEach(function(snippet, index) {
var isCreateIterator = /function createIterator\b/.test(snippet), var isFunc = /^ *function +/m.test(snippet),
isIteratorTemplate = /var iteratorTemplate\b/.test(snippet), isIteratorTemplate = /var iteratorTemplate\b/.test(snippet),
modified = snippet; modified = snippet;
@@ -392,8 +414,8 @@
var minName = minNames[index]; var minName = minNames[index];
// minify variable names present in strings // minify variable names present in strings
if (isCreateIterator) { if (isFunc) {
modified = modified.replace(RegExp('(([\'"])[^\\n\\2]*?)\\b' + varName + '\\b(?=[^\\n\\2]*\\2[ ,+]+$)', 'gm'), '$1' + minName); modified = modified.replace(RegExp('(([\'"])[^\\n\\2]*?)\\b' + varName + '\\b(?=[^\\n\\2]*\\2[ ,+;]+$)', 'gm'), '$1' + minName);
} }
// ensure properties in compiled strings aren't minified // ensure properties in compiled strings aren't minified
else { else {

288
lodash.js
View File

@@ -23,6 +23,10 @@
window = freeGlobal; window = freeGlobal;
} }
/** Used to pool arrays and objects used internally */
var arrayPool = [],
objectPool = [];
/** Used to generate unique IDs */ /** Used to generate unique IDs */
var idCounter = 0; var idCounter = 0;
@@ -139,6 +143,74 @@
'\u2029': 'u2029' '\u2029': 'u2029'
}; };
/**
* Gets an array from the array pool or creates a new one if the pool is empty.
*
* @private
* @returns {Array} The array from the pool.
*/
function getArray() {
return arrayPool.pop() || [];
}
/**
* Gets an object from the object pool or creates a new one if the pool is empty.
*
* @private
* @returns {Object} The object from the pool.
*/
function getObject() {
return objectPool.pop() || {
'args': null,
'array': null,
'arrays': null,
'contains': null,
'criteria': null,
'false': null,
'firstArg': null,
'function': null,
'index': null,
'indexOf': null,
'init': null,
'initArray': null,
'null': null,
'number': null,
'object': null,
'push': null,
'release': null,
'shadowedProps': null,
'string': null,
'support': null,
'true': null,
'undefined': null,
'useHas': null,
'useKeys': null,
'value': null
};
}
/**
* Releases the given `array` back to the array pool.
*
* @private
* @param {Array} [array] The array to release.
*/
function releaseArray(array) {
array.length = 0;
arrayPool.push(array);
}
/**
* Releases the given `object` back to the object pool.
*
* @private
* @param {Object} [object] The object to release.
*/
function releaseObject(object) {
object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
objectPool.push(object);
}
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/** /**
@@ -580,8 +652,8 @@
// iterate own properties using `Object.keys` // iterate own properties using `Object.keys`
' <% if (useHas && useKeys) { %>\n' + ' <% if (useHas && useKeys) { %>\n' +
' var ownIndex = -1,\n' + ' var ownIndex = -1,\n' +
' ownProps = objectTypes[typeof iterable] ? keys(iterable) : [],\n' + ' ownProps = objectTypes[typeof iterable] && keys(iterable),\n' +
' length = ownProps.length;\n\n' + ' length = ownProps ? ownProps.length : 0;\n\n' +
' while (++ownIndex < length) {\n' + ' while (++ownIndex < length) {\n' +
' index = ownProps[ownIndex];\n<%' + ' index = ownProps[ownIndex];\n<%' +
" if (conditions.length) { %> if (<%= conditions.join(' && ') %>) {\n <% } %>" + " if (conditions.length) { %> if (<%= conditions.join(' && ') %>) {\n <% } %>" +
@@ -778,7 +850,6 @@
return bound; return bound;
} }
/** /**
* Creates a function optimized to search large arrays for a given `value`, * Creates a function optimized to search large arrays for a given `value`,
* starting at `fromIndex`, using strict equality for comparisons, i.e. `===`. * starting at `fromIndex`, using strict equality for comparisons, i.e. `===`.
@@ -788,42 +859,28 @@
* @param {Mixed} value The value to search for. * @param {Mixed} value The value to search for.
* @returns {Boolean} Returns `true`, if `value` is found, else `false`. * @returns {Boolean} Returns `true`, if `value` is found, else `false`.
*/ */
function createCache(array) { var createCache = (function() {
array || (array = []);
var bailout,
index = -1,
indexOf = getIndexOf(),
length = array.length,
isLarge = length >= largeArraySize && lodash.indexOf != indexOf,
objCache = {};
var caches = {
'false': false,
'function': false,
'null': false,
'number': {},
'object': objCache,
'string': {},
'true': false,
'undefined': false
};
function basicContains(value) { function basicContains(value) {
return indexOf(array, value) > -1; return this.indexOf(this.array, value) > -1;
} }
function basicPush(value) { function basicPush(value) {
array.push(value); this.array.push(value);
} }
function cacheContains(value) { function cacheContains(value) {
var type = typeof value; var cache = this.cache,
type = typeof value;
if (type == 'boolean' || value == null) { if (type == 'boolean' || value == null) {
return caches[value]; return cache[value];
} }
var cache = caches[type] || (type = 'object', objCache), if (type != 'number' && type != 'string') {
key = type == 'number' ? value : keyPrefix + value; type = 'object';
}
var key = type == 'number' ? value : keyPrefix + value;
cache = cache[type] || (cache[type] = {});
return type == 'object' return type == 'object'
? (cache[key] ? basicIndexOf(cache[key], value) > -1 : false) ? (cache[key] ? basicIndexOf(cache[key], value) > -1 : false)
@@ -831,12 +888,17 @@
} }
function cachePush(value) { function cachePush(value) {
var type = typeof value; var cache = this.cache,
type = typeof value;
if (type == 'boolean' || value == null) { if (type == 'boolean' || value == null) {
caches[value] = true; cache[value] = true;
} else { } else {
var cache = caches[type] || (type = 'object', objCache), if (type != 'number' && type != 'string') {
key = type == 'number' ? value : keyPrefix + value; type = 'object';
}
var key = type == 'number' ? value : keyPrefix + value;
cache = cache[type] || (cache[type] = {});
if (type == 'object') { if (type == 'object') {
bailout = (cache[key] || (cache[key] = [])).push(value) == length; bailout = (cache[key] || (cache[key] = [])).push(value) == length;
@@ -846,18 +908,49 @@
} }
} }
if (isLarge) { function release() {
while (++index < length) { var cache = this.cache;
cachePush(array[index]); if (cache.initArray) {
} releaseArray(this.array);
if (bailout) {
isLarge = caches = objCache = null;
} }
releaseObject(cache);
} }
return isLarge
? { 'contains': cacheContains, 'push': cachePush } return function(array) {
: { 'contains': basicContains, 'push': basicPush }; var bailout,
} index = -1,
initArray = !array && (array = getArray()),
length = array.length,
isLarge = length >= largeArraySize && lodash.indexOf != indexOf;
var cache = getObject();
cache.initArray = initArray;
cache['false'] = cache['function'] = cache['null'] = cache['true'] = cache['undefined'] = false;
var result = getObject();
result.array = array;
result.indexOf = getIndexOf();
result.cache = cache;
result.contains = cacheContains;
result.push = cachePush;
result.release = release;
if (isLarge) {
while (++index < length) {
result.push(array[index]);
}
if (bailout) {
isLarge = false;
result.release();
}
}
if (!isLarge) {
result.contains = basicContains;
result.push = basicPush;
}
return result;
};
}());
/** /**
* Creates compiled iteration functions. * Creates compiled iteration functions.
@@ -874,20 +967,17 @@
* @returns {Function} Returns the compiled function. * @returns {Function} Returns the compiled function.
*/ */
function createIterator() { function createIterator() {
var data = { var data = getObject();
// data properties
'shadowedProps': shadowedProps,
'support': support,
// iterator options // data properties
'arrays': '', data.shadowedProps = shadowedProps;
'bottom': '', data.support = support;
'init': 'iterable',
'loop': '', // iterator options
'top': '', data.arrays = data.bottom = data.loop = data.top = '';
'useHas': true, data.init = 'iterable';
'useKeys': !!keys data.useHas = true;
}; data.useKeys = !!keys;
// merge options into a template data object // merge options into a template data object
for (var object, index = 0; object = arguments[index]; index++) { for (var object, index = 0; object = arguments[index]; index++) {
@@ -900,16 +990,19 @@
// create the function factory // create the function factory
var factory = Function( var factory = Function(
'errorClass, errorProto, hasOwnProperty, isArguments, isArray, isString, ' + 'errorClass, errorProto, hasOwnProperty, isArguments, isArray, ' +
'keys, lodash, objectProto, objectTypes, nonEnumProps, stringClass, ' + 'isString, keys, lodash, objectProto, objectTypes, nonEnumProps, ' +
'stringProto, toString', 'stringClass, stringProto, toString',
'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' 'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
); );
releaseObject(data);
// return the compiled function // return the compiled function
return factory( return factory(
errorClass, errorProto, hasOwnProperty, isArguments, isArray, isString, errorClass, errorProto, hasOwnProperty, isArguments, isArray,
keys, lodash, objectProto, objectTypes, nonEnumProps, stringClass, isString, keys, lodash, objectProto, objectTypes, nonEnumProps,
stringProto, toString stringClass, stringProto, toString
); );
} }
@@ -1368,8 +1461,9 @@
return ctor(result.source, reFlags.exec(result)); return ctor(result.source, reFlags.exec(result));
} }
// check for circular references and return corresponding clone // check for circular references and return corresponding clone
stackA || (stackA = []); var initStack = !stackA;
stackB || (stackB = []); stackA || (stackA = getArray());
stackB || (stackB = getArray());
var length = stackA.length; var length = stackA.length;
while (length--) { while (length--) {
@@ -1399,6 +1493,10 @@
result[key] = clone(objValue, deep, callback, undefined, stackA, stackB); result[key] = clone(objValue, deep, callback, undefined, stackA, stackB);
}); });
if (initStack) {
releaseArray(stackA);
releaseArray(stackB);
}
return result; return result;
} }
@@ -1845,8 +1943,9 @@
// assume cyclic structures are equal // assume cyclic structures are equal
// the algorithm for detecting cyclic structures is adapted from ES 5.1 // the algorithm for detecting cyclic structures is adapted from ES 5.1
// section 15.12.3, abstract operation `JO` (http://es5.github.com/#x15.12.3) // section 15.12.3, abstract operation `JO` (http://es5.github.com/#x15.12.3)
stackA || (stackA = []); var initStack = !stackA;
stackB || (stackB = []); stackA || (stackA = getArray());
stackB || (stackB = getArray());
var length = stackA.length; var length = stackA.length;
while (length--) { while (length--) {
@@ -1908,6 +2007,10 @@
} }
}); });
} }
if (initStack) {
releaseArray(stackA);
releaseArray(stackB);
}
return result; return result;
} }
@@ -2217,8 +2320,9 @@
stackA = args[4], stackA = args[4],
stackB = args[5]; stackB = args[5];
} else { } else {
stackA = []; var initStack = true;
stackB = []; stackA = getArray();
stackB = getArray();
// allows working with `_.reduce` and `_.reduceRight` without // allows working with `_.reduce` and `_.reduceRight` without
// using their `callback` arguments, `index|key` and `collection` // using their `callback` arguments, `index|key` and `collection`
@@ -2284,6 +2388,11 @@
object[key] = value; object[key] = value;
}); });
} }
if (initStack) {
releaseArray(stackA);
releaseArray(stackB);
}
return object; return object;
} }
@@ -3443,17 +3552,18 @@
callback = lodash.createCallback(callback, thisArg); callback = lodash.createCallback(callback, thisArg);
forEach(collection, function(value, key, collection) { forEach(collection, function(value, key, collection) {
result[++index] = { var object = result[++index] = getObject();
'criteria': callback(value, key, collection), object.criteria = callback(value, key, collection);
'index': index, object.index = index;
'value': value object.value = value;
};
}); });
length = result.length; length = result.length;
result.sort(compareAscending); result.sort(compareAscending);
while (length--) { while (length--) {
result[length] = result[length].value; var object = result[length];
result[length] = object.value;
releaseObject(object);
} }
return result; return result;
} }
@@ -3555,15 +3665,16 @@
var index = -1, var index = -1,
length = array ? array.length : 0, length = array ? array.length : 0,
flattened = concat.apply(arrayProto, nativeSlice.call(arguments, 1)), flattened = concat.apply(arrayProto, nativeSlice.call(arguments, 1)),
contains = createCache(flattened).contains, cache = createCache(flattened),
result = []; result = [];
while (++index < length) { while (++index < length) {
var value = array[index]; var value = array[index];
if (!contains(value)) { if (!cache.contains(value)) {
result.push(value); result.push(value);
} }
} }
cache.release();
return result; return result;
} }
@@ -3867,27 +3978,35 @@
function intersection(array) { function intersection(array) {
var args = arguments, var args = arguments,
argsLength = args.length, argsLength = args.length,
cache = createCache(), caches = getArray(),
caches = {},
index = -1, index = -1,
length = array ? array.length : 0, length = array ? array.length : 0,
isLarge = length >= largeArraySize, isLarge = length >= largeArraySize,
result = []; result = [];
caches[0] = createCache();
outer: outer:
while (++index < length) { while (++index < length) {
var value = array[index]; var cache = caches[0],
value = array[index];
if (!cache.contains(value)) { if (!cache.contains(value)) {
var argsIndex = argsLength; var argsIndex = argsLength;
cache.push(value); cache.push(value);
while (--argsIndex) { while (--argsIndex) {
if (!(caches[argsIndex] || (caches[argsIndex] = createCache(args[argsIndex]).contains))(value)) { cache = caches[argsIndex] || (caches[argsIndex] = createCache(args[argsIndex]));
if (!cache.contains(value)) {
continue outer; continue outer;
} }
} }
result.push(value); result.push(value);
} }
} }
while (argsLength--) {
caches[argsLength].release();
}
releaseArray(caches);
return result; return result;
} }
@@ -4257,11 +4376,11 @@
*/ */
var uniq = overloadWrapper(function(array, isSorted, callback) { var uniq = overloadWrapper(function(array, isSorted, callback) {
var index = -1, var index = -1,
indexOf = getIndexOf(),
length = array ? array.length : 0, length = array ? array.length : 0,
isLarge = !isSorted && length >= largeArraySize, isLarge = !isSorted && length >= largeArraySize,
indexOf = isLarge || getIndexOf(),
result = [], result = [],
seen = isLarge ? createCache() : (callback ? [] : result); seen = isLarge ? createCache() : (callback ? getArray() : result);
while (++index < length) { while (++index < length) {
var value = array[index], var value = array[index],
@@ -4277,6 +4396,11 @@
result.push(value); result.push(value);
} }
} }
if (isLarge) {
seen.release();
} else if (callback) {
releaseArray(seen);
}
return result; return result;
}); });