Reduce temporary objects created in _.merge, _.clone, and _.isEqual.

Former-commit-id: e6696642505f39eefdf59075ff8a993ab033465a
This commit is contained in:
John-David Dalton
2012-09-10 20:12:42 -07:00
parent 4fc3c969d3
commit 2afb2dd5fd
2 changed files with 42 additions and 33 deletions

View File

@@ -44,7 +44,7 @@
'callee', 'callee',
'className', 'className',
'compareAscending', 'compareAscending',
'destValue', 'data',
'forIn', 'forIn',
'found', 'found',
'funcs', 'funcs',
@@ -66,10 +66,11 @@
'propsLength', 'propsLength',
'recursive', 'recursive',
'source', 'source',
'stack', 'sources',
'stackLength', 'stackLength',
'target', 'target',
'valueProp' 'valueProp',
'values'
]; ];
/** Used to minify `compileIterator` option properties */ /** Used to minify `compileIterator` option properties */
@@ -107,18 +108,17 @@
'__chain__', '__chain__',
'__proto__', '__proto__',
'__wrapped__', '__wrapped__',
'a',
'after', 'after',
'all', 'all',
'amd', 'amd',
'any', 'any',
'attachEvent', 'attachEvent',
'b',
'bind', 'bind',
'bindAll', 'bindAll',
'chain', 'chain',
'clearTimeout', 'clearTimeout',
'clone', 'clone',
'clones',
'collect', 'collect',
'compact', 'compact',
'compose', 'compose',
@@ -214,6 +214,9 @@
'sortBy', 'sortBy',
'sortedIndex', 'sortedIndex',
'source', 'source',
'sources',
'stackA',
'stackB',
'tail', 'tail',
'take', 'take',
'tap', 'tap',
@@ -304,7 +307,7 @@
// minify internal properties used by 'compareAscending', `_.clone`, `_.isEqual`, `_.merge`, and `_.sortBy` // minify internal properties used by 'compareAscending', `_.clone`, `_.isEqual`, `_.merge`, and `_.sortBy`
(function() { (function() {
var properties = ['criteria', 'index', 'source', 'thorough', 'value'], var properties = ['clones', 'criteria', 'index', 'sources', 'thorough', 'value', 'values'],
snippets = source.match(/( +)(?:function (?:clone|compareAscending|isEqual)|var merge|var sortBy)\b[\s\S]+?\n\1}/g); snippets = source.match(/( +)(?:function (?:clone|compareAscending|isEqual)|var merge|var sortBy)\b[\s\S]+?\n\1}/g);
if (!snippets) { if (!snippets) {
@@ -319,7 +322,7 @@
properties.forEach(function(property, index) { properties.forEach(function(property, index) {
var reBracketProp = RegExp("\\['(" + property + ")'\\]", 'g'), var reBracketProp = RegExp("\\['(" + property + ")'\\]", 'g'),
reDotProp = RegExp('\\.' + property + '\\b', 'g'), reDotProp = RegExp('\\.' + property + '\\b', 'g'),
rePropColon = RegExp("(')?\\b" + property + "\\1 *:", 'g'); rePropColon = RegExp("([^?])(')?\\b" + property + "\\2 *:", 'g');
if (isCompilable) { if (isCompilable) {
// add quotes around properties in the inlined `_.merge` and `_.sortBy` // add quotes around properties in the inlined `_.merge` and `_.sortBy`
@@ -328,20 +331,20 @@
modified = modified modified = modified
.replace(reBracketProp, "['" + minNames[index] + "']") .replace(reBracketProp, "['" + minNames[index] + "']")
.replace(reDotProp, "['" + minNames[index] + "']") .replace(reDotProp, "['" + minNames[index] + "']")
.replace(rePropColon, "'" + minNames[index] + "':"); .replace(rePropColon, "$1'" + minNames[index] + "':");
} }
else { else {
modified = modified modified = modified
.replace(reBracketProp, '.' + minNames[index]) .replace(reBracketProp, '.' + minNames[index])
.replace(reDotProp, '.' + minNames[index]) .replace(reDotProp, '.' + minNames[index])
.replace(rePropColon, minNames[index] + ':'); .replace(rePropColon, '$1' + minNames[index] + ':');
} }
} }
else { else {
modified = modified modified = modified
.replace(reBracketProp, "['" + minNames[index] + "']") .replace(reBracketProp, "['" + minNames[index] + "']")
.replace(reDotProp, '.' + minNames[index]) .replace(reDotProp, '.' + minNames[index])
.replace(rePropColon, "'" + minNames[index] + "':") .replace(rePropColon, "$1'" + minNames[index] + "':")
// correct `value.source` in regexp branch of `_.clone` // correct `value.source` in regexp branch of `_.clone`
if (property == 'source') { if (property == 'source') {

View File

@@ -1141,13 +1141,14 @@
return ctor(value.source, reFlags.exec(value)); return ctor(value.source, reFlags.exec(value));
} }
var stack = data.stack || (data.stack = []), var clones = data.clones || (data.clones = []),
length = stack.length; sources = data.sources || (data.sources = []),
length = clones.length;
// check for circular references and return corresponding clone // check for circular references and return corresponding clone
while (length--) { while (length--) {
if (stack[length].source == value) { if (sources[length] == value) {
return stack[length].value; return clones[length];
} }
} }
@@ -1155,7 +1156,8 @@
var result = isArr ? ctor(length = value.length) : {}; var result = isArr ? ctor(length = value.length) : {};
// add current clone and original source value to the stack of traversed objects // add current clone and original source value to the stack of traversed objects
stack.push({ 'value': result, 'source': value }); clones.push(result);
sources.push(value);
// recursively populate clone (susceptible to call stack limits) // recursively populate clone (susceptible to call stack limits)
if (isArr) { if (isArr) {
@@ -1512,12 +1514,13 @@
// 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)
var stack = data.stack || (data.stack = []), var stackA = data.stackA || (data.stackA = []),
length = stack.length; stackB = data.stackB || (data.stackB = []),
length = stackA.length;
while (length--) { while (length--) {
if (stack[length].a == a) { if (stackA[length] == a) {
return stack[length].b == b; return stackA[length] == b;
} }
} }
@@ -1526,7 +1529,8 @@
size = 0; size = 0;
// add `a` and `b` to the stack of traversed objects // add `a` and `b` to the stack of traversed objects
stack.push({ 'a': a, 'b': b }); stackA.push(a);
stackB.push(b);
// recursively compare objects and arrays (susceptible to call stack limits) // recursively compare objects and arrays (susceptible to call stack limits)
if (isArr) { if (isArr) {
@@ -1800,7 +1804,7 @@
* @param {Object} [source1, source2, ...] The source objects. * @param {Object} [source1, source2, ...] The source objects.
* @param {Object} [indicator] Internally used to indicate that the `stack` * @param {Object} [indicator] Internally used to indicate that the `stack`
* argument is an array of traversed objects instead of another source object. * argument is an array of traversed objects instead of another source object.
* @param {Array} [stack=[]] Internally used to track traversed objects to avoid * @param {Object} [data={}] Internally used to track traversed objects to avoid
* circular references. * circular references.
* @returns {Object} Returns the destination object. * @returns {Object} Returns the destination object.
* @example * @example
@@ -1821,26 +1825,28 @@
var merge = createIterator(extendIteratorOptions, { var merge = createIterator(extendIteratorOptions, {
'args': 'object, source, indicator', 'args': 'object, source, indicator',
'top': 'top':
'var isArr, recursive = indicator == isPlainObject, stack = recursive ? arguments[3] : [];\n' + 'var isArr, source, recursive = indicator == isPlainObject,\n' +
' data = recursive ? arguments[3] : { values: [], sources: [] };\n' +
'for (var argsIndex = 1, argsLength = recursive ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n' + 'for (var argsIndex = 1, argsLength = recursive ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n' +
' if (iteratee = arguments[argsIndex]) {', ' if (iteratee = arguments[argsIndex]) {',
'inLoop': 'inLoop':
'if (value && ((isArr = isArray(value)) || isPlainObject(value))) {\n' + 'if ((source = value) && ((isArr = isArray(source)) || isPlainObject(source))) {\n' +
' var found = false, stackLength = stack.length;\n' + ' var found = false, values = data.values, sources = data.sources, stackLength = sources.length;\n' +
' while (stackLength--) {\n' + ' while (stackLength--) {\n' +
' if (found = stack[stackLength].source == value) break\n' + ' if (found = sources[stackLength] == source) break\n' +
' }\n' + ' }\n' +
' if (found) {\n' + ' if (found) {\n' +
' result[index] = stack[stackLength].value\n' + ' result[index] = values[stackLength]\n' +
' } else {\n' + ' } else {\n' +
' var destValue = (destValue = result[index]) && isArr\n' + ' sources.push(source);\n' +
' ? (isArray(destValue) ? destValue : [])\n' + ' values.push(value = (value = result[index]) && isArr\n' +
' : (isPlainObject(destValue) ? destValue : {});\n' + ' ? (isArray(value) ? value : [])\n' +
' stack.push({ value: destValue, source: value });\n' + ' : (isPlainObject(value) ? value : {})\n' +
' result[index] = callee(destValue, value, isPlainObject, stack)\n' + ' );\n' +
' result[index] = callee(value, source, isPlainObject, data)\n' +
' }\n' + ' }\n' +
'} else if (value != null) {\n' + '} else if (source != null) {\n' +
' result[index] = value\n' + ' result[index] = source\n' +
'}' '}'
}); });