Make remove compiling from _.merge, _.countBy, _.groupBy, _.pick, _.omit, and _.sortBy.

Former-commit-id: 52b245e69629e7a9fbe5f0dcbdfafabcd75d9dfc
This commit is contained in:
John-David Dalton
2012-10-13 00:03:40 -07:00
parent 10c87012be
commit 1dda31a28c
3 changed files with 144 additions and 170 deletions

View File

@@ -71,7 +71,7 @@
'compact': [], 'compact': [],
'compose': [], 'compose': [],
'contains': [], 'contains': [],
'countBy': ['identity'], 'countBy': ['forEach', 'identity'],
'debounce': [], 'debounce': [],
'defaults': ['isArguments'], 'defaults': ['isArguments'],
'defer': [], 'defer': [],
@@ -88,7 +88,7 @@
'forIn': ['identity', 'isArguments'], 'forIn': ['identity', 'isArguments'],
'forOwn': ['identity', 'isArguments'], 'forOwn': ['identity', 'isArguments'],
'functions': ['isArguments', 'isFunction'], 'functions': ['isArguments', 'isFunction'],
'groupBy': ['identity'], 'groupBy': ['forEach', 'identity'],
'has': [], 'has': [],
'identity': [], 'identity': [],
'indexOf': ['sortedIndex'], 'indexOf': ['sortedIndex'],
@@ -120,16 +120,16 @@
'map': ['identity'], 'map': ['identity'],
'max': ['forEach'], 'max': ['forEach'],
'memoize': [], 'memoize': [],
'merge': ['isArray', 'isPlainObject'], 'merge': ['forOwn', 'isArray', 'isPlainObject'],
'min': ['forEach'], 'min': ['forEach'],
'mixin': ['forEach', 'functions'], 'mixin': ['forEach', 'functions'],
'noConflict': [], 'noConflict': [],
'object': [], 'object': [],
'omit': ['indexOf', 'isArguments'], 'omit': ['forIn', 'indexOf', 'isArguments'],
'once': [], 'once': [],
'pairs': [], 'pairs': [],
'partial': ['isFunction'], 'partial': ['isFunction'],
'pick': [], 'pick': ['forIn'],
'pluck': [], 'pluck': [],
'random': [], 'random': [],
'range': [], 'range': [],
@@ -141,7 +141,7 @@
'shuffle': ['forEach'], 'shuffle': ['forEach'],
'size': ['keys'], 'size': ['keys'],
'some': ['identity'], 'some': ['identity'],
'sortBy': ['identity'], 'sortBy': ['forEach', 'identity'],
'sortedIndex': ['identity'], 'sortedIndex': ['identity'],
'tap': ['mixin'], 'tap': ['mixin'],
'template': ['escape'], 'template': ['escape'],
@@ -1247,11 +1247,6 @@
source = source.replace(reFunc, '$1' + getFunctionSource(lodash[methodName]) + ';\n'); source = source.replace(reFunc, '$1' + getFunctionSource(lodash[methodName]) + ';\n');
}); });
// replace `callee` in `_.merge` with `merge`
source = source.replace(matchFunction(source, 'merge'), function(match) {
return match.replace(/\bcallee\b/g, 'merge');
});
if (isUnderscore) { if (isUnderscore) {
// remove "compiled template cleanup" from `_.template` // remove "compiled template cleanup" from `_.template`
source = source.replace(/(?:\s*\/\/.*)*\n *source *=.+?isEvaluating.+?reEmptyStringLeading[\s\S]+?\);/, ''); source = source.replace(/(?:\s*\/\/.*)*\n *source *=.+?isEvaluating.+?reEmptyStringLeading[\s\S]+?\);/, '');

View File

@@ -11,7 +11,6 @@
'argsLength', 'argsLength',
'callback', 'callback',
'collection', 'collection',
'concat',
'createCallback', 'createCallback',
'ctor', 'ctor',
'hasOwnProperty', 'hasOwnProperty',
@@ -36,38 +35,22 @@
'value', 'value',
// lesser used variables // lesser used variables
'accumulator',
'args', 'args',
'arrayLikeClasses', 'arrayLikeClasses',
'ArrayProto',
'bind', 'bind',
'callee',
'className', 'className',
'compareAscending',
'forIn', 'forIn',
'found',
'funcs', 'funcs',
'indexOf',
'indicator',
'isArguments', 'isArguments',
'isArr',
'isArray',
'isFunc', 'isFunc',
'isFunction', 'isFunction',
'isPlainObject',
'methodName', 'methodName',
'noaccum',
'noop',
'objectClass', 'objectClass',
'objectTypes', 'objectTypes',
'pass', 'pass',
'properties', 'properties',
'property', 'property',
'propsLength', 'propsLength',
'source',
'stackA',
'stackB',
'stackLength',
'target' 'target'
]; ];
@@ -310,15 +293,13 @@
// minify internal properties used by 'compareAscending', `_.merge`, and `_.sortBy` // minify internal properties used by 'compareAscending', `_.merge`, and `_.sortBy`
(function() { (function() {
var properties = ['criteria', 'index', 'value'], var properties = ['criteria', 'index', 'value'],
snippets = source.match(/( +)(?:function compareAscending|var merge|var sortBy)\b[\s\S]+?\n\1}/g); snippets = source.match(/( +)function (?:compareAscending|merge|sortBy)\b[\s\S]+?\n\1}/g);
if (!snippets) { if (!snippets) {
return; return;
} }
snippets.forEach(function(snippet) { snippets.forEach(function(snippet) {
var modified = snippet, var modified = snippet;
isCompilable = /(?:var merge|var sortBy)\b/.test(modified),
isInlined = !/\bcreateIterator\b/.test(modified);
// minify properties // minify properties
properties.forEach(function(property, index) { properties.forEach(function(property, index) {
@@ -326,32 +307,14 @@
reDotProp = RegExp('\\.' + property + '\\b', 'g'), reDotProp = RegExp('\\.' + property + '\\b', 'g'),
rePropColon = RegExp("([^?\\s])\\s*([\"'])?\\b" + property + "\\2 *:", 'g'); rePropColon = RegExp("([^?\\s])\\s*([\"'])?\\b" + property + "\\2 *:", 'g');
if (isCompilable) { modified = modified
// add quotes around properties in the inlined `_.merge` and `_.sortBy` .replace(reBracketProp, "['" + minNames[index] + "']")
// of the mobile build so Closure Compiler won't mung them .replace(reDotProp, '.' + minNames[index])
if (isInlined) { .replace(rePropColon, "$1'" + minNames[index] + "':")
modified = modified
.replace(reBracketProp, "['" + minNames[index] + "']")
.replace(reDotProp, "['" + minNames[index] + "']")
.replace(rePropColon, "$1'" + minNames[index] + "':");
}
else {
modified = modified
.replace(reBracketProp, '.' + minNames[index])
.replace(reDotProp, '.' + minNames[index])
.replace(rePropColon, '$1' + minNames[index] + ':');
}
}
else {
modified = modified
.replace(reBracketProp, "['" + minNames[index] + "']")
.replace(reDotProp, '.' + 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') {
modified = modified.replace("value['" + minNames[index] + "']", "value['source']"); modified = modified.replace("value['" + minNames[index] + "']", "value['source']");
}
} }
}); });

242
lodash.js
View File

@@ -425,9 +425,8 @@
); );
/** /**
* Reusable iterator options shared by * Reusable iterator options shared by `every`, `filter`,
* `countBy`, `every`, `filter`, `find`, `forEach`, `forIn`, `forOwn`, `groupBy`, * `find`, `forEach`, `forIn`, `forOwn`, `map`, `reject`, and `some`.
* `map`, `reject`, `some`, and `sortBy`.
*/ */
var baseIteratorOptions = { var baseIteratorOptions = {
'args': 'collection, callback, thisArg', 'args': 'collection, callback, thisArg',
@@ -435,15 +434,6 @@
'inLoop': 'if (callback(value, index, collection) === false) return result' 'inLoop': 'if (callback(value, index, collection) === false) return result'
}; };
/** Reusable iterator options for `countBy`, `groupBy`, and `sortBy` */
var countByIteratorOptions = {
'init': '{}',
'top': 'callback = createCallback(callback, thisArg)',
'inLoop':
'var prop = callback(value, index, collection);\n' +
'(hasOwnProperty.call(result, prop) ? result[prop]++ : result[prop] = 1)'
};
/** Reusable iterator options for `every` and `some` */ /** Reusable iterator options for `every` and `some` */
var everyIteratorOptions = { var everyIteratorOptions = {
'init': 'true', 'init': 'true',
@@ -480,7 +470,7 @@
} }
}; };
/** Reusable iterator options for `invoke`, `map`, `pluck`, and `sortBy` */ /** Reusable iterator options for `invoke`, `map`, and `pluck` */
var mapIteratorOptions = { var mapIteratorOptions = {
'init': 'collection || []', 'init': 'collection || []',
'beforeLoop': { 'beforeLoop': {
@@ -493,22 +483,6 @@
} }
}; };
/** Reusable iterator options for `omit` and `pick` */
var omitIteratorOptions = {
'useHas': false,
'args': 'object, callback, thisArg',
'init': '{}',
'top':
'var isFunc = typeof callback == \'function\';\n' +
'if (isFunc) callback = createCallback(callback, thisArg);\n' +
'else var props = concat.apply(ArrayProto, arguments)',
'inLoop':
'if (isFunc\n' +
' ? !callback(value, index, object)\n' +
' : indexOf(props, index) < 0\n' +
') result[index] = value'
};
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/** /**
@@ -727,18 +701,15 @@
} }
// create the function factory // create the function factory
var factory = Function( var factory = Function(
'arrayLikeClasses, ArrayProto, bind, compareAscending, concat, createCallback, ' + 'arrayLikeClasses, bind, createCallback, forIn, hasOwnProperty, isArguments, ' +
'forIn, hasOwnProperty, indexOf, isArguments, isArray, isFunction, ' + 'isFunction, objectClass, objectTypes, nativeKeys, propertyIsEnumerable, ' +
'isPlainObject, objectClass, objectTypes, nativeKeys, propertyIsEnumerable, ' +
'slice, stringClass, toString, undefined', 'slice, stringClass, toString, undefined',
'var callee = function(' + args + ') {\n' + iteratorTemplate(data) + '\n};\n' + 'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
'return callee'
); );
// return the compiled function // return the compiled function
return factory( return factory(
arrayLikeClasses, ArrayProto, bind, compareAscending, concat, createCallback, arrayLikeClasses, bind, createCallback, forIn, hasOwnProperty, isArguments,
forIn, hasOwnProperty, indexOf, isArguments, isArray, isFunction, isFunction, objectClass, objectTypes, nativeKeys, propertyIsEnumerable,
isPlainObject, objectClass, objectTypes, nativeKeys, propertyIsEnumerable,
slice, stringClass, toString slice, stringClass, toString
); );
} }
@@ -1705,37 +1676,48 @@
* _.merge(stooges, ages); * _.merge(stooges, ages);
* // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }] * // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }]
*/ */
var merge = createIterator(extendIteratorOptions, { function merge(object, source, indicator) {
'args': 'object, source, indicator', var args = arguments,
'top': index = 0,
'var isArr, args = arguments, argsIndex = 0;\n' + length = 2,
'if (indicator == compareAscending) {\n' + stackA = args[3],
' var argsLength = 2, stackA = args[3], stackB = args[4]\n' + stackB = args[4];
'} else {\n' +
' var argsLength = args.length, stackA = [], stackB = []\n' + if (indicator != compareAscending) {
'}\n' + length = args.length;
'while (++argsIndex < argsLength) {\n' + stackA = [];
' if (iteratee = args[argsIndex]) {', stackB = [];
'inLoop': }
'if ((source = value) && ((isArr = isArray(source)) || isPlainObject(source))) {\n' + while (++index < length) {
' var found = false, stackLength = stackA.length;\n' + forOwn(args[index], function(source, key) {
' while (stackLength--) {\n' + var isArr, value;
' if (found = stackA[stackLength] == source) break\n' + if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
' }\n' + var found = false,
' if (found) {\n' + stackLength = stackA.length;
' result[index] = stackB[stackLength]\n' +
' } else {\n' + while (stackLength--) {
' stackA.push(source);\n' + if ((found = stackA[stackLength] == source)) {
' stackB.push(value = (value = result[index], isArr)\n' + break;
' ? (isArray(value) ? value : [])\n' + }
' : (isPlainObject(value) ? value : {})\n' + }
' );\n' + if (found) {
' result[index] = callee(value, source, compareAscending, stackA, stackB)\n' + object[key] = stackB[stackLength];
' }\n' + }
'} else if (source != null) {\n' + else {
' result[index] = source\n' + stackA.push(source);
'}' stackB.push(value = (value = object[key], isArr)
}); ? (isArray(value) ? value : [])
: (isPlainObject(value) ? value : {})
);
object[key] = merge(value, source, compareAscending, stackA, stackB);
}
} else if (source != null) {
object[key] = source;
}
});
}
return object;
}
/** /**
* Creates a shallow clone of `object` excluding the specified properties. * Creates a shallow clone of `object` excluding the specified properties.
@@ -1762,7 +1744,25 @@
* }); * });
* // => { 'name': 'moe' } * // => { 'name': 'moe' }
*/ */
var omit = createIterator(omitIteratorOptions); function omit(object, callback, thisArg) {
var isFunc = typeof callback == 'function',
result = {};
if (isFunc) {
callback = createCallback(callback, thisArg);
} else {
var props = concat.apply(ArrayProto, arguments);
}
forIn(object, function(value, key, object) {
if (isFunc
? !callback(value, key, object)
: indexOf(props, key) < 0
) {
result[key] = value;
}
});
return result;
}
/** /**
* Creates a two dimensional array of the given object's key-value pairs, * Creates a two dimensional array of the given object's key-value pairs,
@@ -1809,22 +1809,29 @@
* }); * });
* // => { 'name': 'moe' } * // => { 'name': 'moe' }
*/ */
var pick = createIterator(omitIteratorOptions, { function pick(object, callback, thisArg) {
'top': var result = {};
'if (typeof callback != \'function\') {\n' + if (typeof callback != 'function') {
' var index = 0,\n' + var index = 0,
' props = concat.apply(ArrayProto, arguments),\n' + props = concat.apply(ArrayProto, arguments),
' length = props.length;\n' + length = props.length;
' while (++index < length) {\n' +
' var prop = props[index];\n' + while (++index < length) {
' if (prop in object) result[prop] = object[prop]\n' + var prop = props[index];
' }\n' + if (prop in object) {
'} else {\n' + result[prop] = object[prop];
' callback = createCallback(callback, thisArg)', }
'inLoop': }
'if (callback(value, index, object)) result[index] = value', } else {
'bottom': '}' callback = createCallback(callback, thisArg);
}); forIn(object, function(value, key, object) {
if (callback(value, key, object)) {
result[key] = value;
}
});
}
return result;
}
/** /**
* Creates an array composed of the own enumerable property values of `object`. * Creates an array composed of the own enumerable property values of `object`.
@@ -1905,7 +1912,15 @@
* _.countBy(['one', 'two', 'three'], 'length'); * _.countBy(['one', 'two', 'three'], 'length');
* // => { '3': 2, '5': 1 } * // => { '3': 2, '5': 1 }
*/ */
var countBy = createIterator(baseIteratorOptions, countByIteratorOptions); function countBy(collection, callback, thisArg) {
var result = {};
callback = createCallback(callback, thisArg);
forEach(collection, function(value, key, collection) {
key = callback(value, key, collection);
(hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
});
return result;
}
/** /**
* Checks if the `callback` returns a truthy value for **all** elements of a * Checks if the `callback` returns a truthy value for **all** elements of a
@@ -2023,11 +2038,15 @@
* _.groupBy(['one', 'two', 'three'], 'length'); * _.groupBy(['one', 'two', 'three'], 'length');
* // => { '3': ['one', 'two'], '5': ['three'] } * // => { '3': ['one', 'two'], '5': ['three'] }
*/ */
var groupBy = createIterator(baseIteratorOptions, countByIteratorOptions, { function groupBy(collection, callback, thisArg) {
'inLoop': var result = {};
'var prop = callback(value, index, collection);\n' + callback = createCallback(callback, thisArg);
'(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(value)' forEach(collection, function(value, key, collection) {
}); key = callback(value, key, collection);
(hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
});
return result;
}
/** /**
* Invokes the method named by `methodName` on each element in the `collection`, * Invokes the method named by `methodName` on each element in the `collection`,
@@ -2408,28 +2427,25 @@
* _.sortBy(['larry', 'brendan', 'moe'], 'length'); * _.sortBy(['larry', 'brendan', 'moe'], 'length');
* // => ['moe', 'larry', 'brendan'] * // => ['moe', 'larry', 'brendan']
*/ */
var sortBy = createIterator(baseIteratorOptions, countByIteratorOptions, mapIteratorOptions, { function sortBy(collection, callback, thisArg) {
'inLoop': { var result = [];
'array': callback = createCallback(callback, thisArg);
'result[index] = {\n' +
' criteria: callback(value, index, collection),\n' + forEach(collection, function(value, index, collection) {
' index: index,\n' + result.push({
' value: value\n' + 'criteria': callback(value, index, collection),
'}', 'index': index,
'object': 'value': value
'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '({\n' + });
' criteria: callback(value, index, collection),\n' + });
' index: index,\n' +
' value: value\n' + var length = result.length;
'})' result.sort(compareAscending);
}, while (length--) {
'bottom': result[length] = result[length].value;
'result.sort(compareAscending);\n' + }
'length = result.length;\n' + return result;
'while (length--) {\n' + }
' result[length] = result[length].value\n' +
'}'
});
/** /**
* Converts the `collection`, to an array. * Converts the `collection`, to an array.