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': [],
'compose': [],
'contains': [],
'countBy': ['identity'],
'countBy': ['forEach', 'identity'],
'debounce': [],
'defaults': ['isArguments'],
'defer': [],
@@ -88,7 +88,7 @@
'forIn': ['identity', 'isArguments'],
'forOwn': ['identity', 'isArguments'],
'functions': ['isArguments', 'isFunction'],
'groupBy': ['identity'],
'groupBy': ['forEach', 'identity'],
'has': [],
'identity': [],
'indexOf': ['sortedIndex'],
@@ -120,16 +120,16 @@
'map': ['identity'],
'max': ['forEach'],
'memoize': [],
'merge': ['isArray', 'isPlainObject'],
'merge': ['forOwn', 'isArray', 'isPlainObject'],
'min': ['forEach'],
'mixin': ['forEach', 'functions'],
'noConflict': [],
'object': [],
'omit': ['indexOf', 'isArguments'],
'omit': ['forIn', 'indexOf', 'isArguments'],
'once': [],
'pairs': [],
'partial': ['isFunction'],
'pick': [],
'pick': ['forIn'],
'pluck': [],
'random': [],
'range': [],
@@ -141,7 +141,7 @@
'shuffle': ['forEach'],
'size': ['keys'],
'some': ['identity'],
'sortBy': ['identity'],
'sortBy': ['forEach', 'identity'],
'sortedIndex': ['identity'],
'tap': ['mixin'],
'template': ['escape'],
@@ -1247,11 +1247,6 @@
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) {
// remove "compiled template cleanup" from `_.template`
source = source.replace(/(?:\s*\/\/.*)*\n *source *=.+?isEvaluating.+?reEmptyStringLeading[\s\S]+?\);/, '');

View File

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

242
lodash.js
View File

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