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

View File

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

288
lodash.js
View File

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