Simplify iteratorTemplate.

This commit is contained in:
John-David Dalton
2013-12-14 00:29:40 -08:00
parent 60a1374fb4
commit e3b4b2f667
7 changed files with 314 additions and 446 deletions

211
dist/lodash.compat.js vendored
View File

@@ -126,21 +126,6 @@
'writable': false
};
/** Used as the data object for `iteratorTemplate` */
var iteratorData = {
'args': '',
'array': null,
'bottom': '',
'firstArg': '',
'init': '',
'keys': null,
'loop': '',
'shadowedProps': null,
'support': null,
'top': '',
'useHas': false
};
/** Used to determine if values are of the language type Object */
var objectTypes = {
'boolean': false,
@@ -892,91 +877,58 @@
*/
var iteratorTemplate = function(obj) {
var __p = 'var index, iterable = ' +
(obj.firstArg) +
', result = ' +
var __p = 'var result = ' +
(obj.init) +
';\nif (!iterable) return result;\n' +
';\nif (!(object && objectTypes[typeof object])) return result;\n' +
(obj.top) +
';';
if (obj.array) {
__p += '\nvar length = iterable.length; index = -1;\nif (' +
(obj.array) +
') { ';
if (support.unindexedChars) {
__p += '\n if (isString(iterable)) {\n iterable = iterable.split(\'\')\n } ';
}
__p += '\n while (++index < length) {\n ' +
if (support.nonEnumArgs) {
__p += '\nvar length = object.length;\nif (length && isArguments(object)) {\n key = -1;\n while (++key < length) {\n key += \'\';\n ' +
(obj.loop) +
';\n }\n}\nelse { ';
} else if (support.nonEnumArgs) {
__p += '\n var length = iterable.length; index = -1;\n if (length && isArguments(iterable)) {\n while (++index < length) {\n index += \'\';\n ' +
(obj.loop) +
';\n }\n } else { ';
';\n }\n return result\n}';
}
if (support.enumPrototypes) {
__p += '\n var skipProto = typeof iterable == \'function\';\n ';
__p += '\nvar skipProto = typeof object == \'function\';\n';
}
if (support.enumErrorProps) {
__p += '\n var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n ';
__p += '\nvar skipErrorProps = object === errorProto || object instanceof Error;\n';
}
var conditions = []; if (support.enumPrototypes) { conditions.push('!(skipProto && index == "prototype")'); } if (support.enumErrorProps) { conditions.push('!(skipErrorProps && (index == "message" || index == "name"))'); }
if (obj.useHas && obj.keys) {
__p += '\n var ownIndex = -1,\n ownProps = keys(iterable),\n length = ownProps.length;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n';
if (conditions.length) {
var conditions = [];
if (support.enumPrototypes) { conditions.push('!(skipProto && key == \'prototype\')'); }
if (support.enumErrorProps) { conditions.push('!(skipErrorProps && (key == \'message\' || key == \'name\'))'); }
__p += '\nfor (var key in object) {\n';
if (obj.useHas) { conditions.push('hasOwnProperty.call(object, key)'); }
if (conditions.length) {
__p += ' if (' +
(conditions.join(' && ')) +
') {\n ';
}
__p +=
(obj.loop) +
'; ';
'; ';
if (conditions.length) {
__p += '\n }';
}
__p += '\n } ';
} else {
__p += '\n for (index in iterable) {\n';
if (obj.useHas) { conditions.push("hasOwnProperty.call(iterable, index)"); } if (conditions.length) {
__p += ' if (' +
(conditions.join(' && ')) +
') {\n ';
}
__p +=
(obj.loop) +
'; ';
if (conditions.length) {
__p += '\n }';
}
__p += '\n } ';
__p += '\n}\n';
if (support.nonEnumShadows) {
__p += '\n\n if (iterable !== objectProto) {\n var ctor = iterable.constructor,\n isProto = iterable === (ctor && ctor.prototype),\n className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n nonEnum = nonEnumProps[className];\n ';
for (k = 0; k < 7; k++) {
__p += '\n index = \'' +
(obj.shadowedProps[k]) +
'\';\n if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))';
if (!obj.useHas) {
__p += ' || (!nonEnum[index] && iterable[index] !== objectProto[index])';
__p += '\nif (object !== objectProto) {\n var ctor = object.constructor,\n isProto = object === (ctor && ctor.prototype),\n className = object === stringProto ? stringClass : object === errorProto ? errorClass : toString.call(object),\n nonEnum = nonEnumProps[className];\n ';
for (var index = 0; index < 7; index++) {
__p += '\n key = \'' +
(obj.shadowedProps[index]) +
'\';\n if ((!(isProto && nonEnum[key]) && hasOwnProperty.call(object, key))';
if (!obj.useHas) {
__p += ' || (!nonEnum[key] && object[key] !== objectProto[key])';
}
__p += ') {\n ' +
__p += ') {\n ' +
(obj.loop) +
';\n } ';
';\n } ';
}
__p += '\n } ';
}
}
if (obj.array || support.nonEnumArgs) {
__p += '\n}';
}
__p +=
(obj.bottom) +
';\nreturn result';
__p += '\nreturn result';
return __p
};
@@ -1285,6 +1237,40 @@
return result;
}
/**
* Iterates `arguments` objects, arrays, objects, and strings consistently
* across environments, executing the callback for each element in the
* collection. The callback is bound to `thisArg` and invoked with three
* arguments; (value, index|key, collection). Callbacks may exit iteration
* early by explicitly returning `false`.
*
* @private
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Array|Object|string} Returns `collection`.
*/
function baseEach(collection, callback, thisArg) {
var index = -1,
iterable = collection,
length = collection ? collection.length : 0;
callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
if (typeof length == 'number') {
if (support.unindexedChars && isString(iterable)) {
iterable = iterable.split('');
}
while (++index < length) {
if (callback(iterable[index], index, collection) === false) {
break;
}
}
} else {
forOwn(collection, callback);
}
return collection;
}
/**
* The base implementation of `_.flatten` without support for callback
* shorthands or `thisArg` binding.
@@ -1741,47 +1727,27 @@
* Creates compiled iteration functions.
*
* @private
* @param {...Object} [options] The compile options object(s).
* @param {string} [options.array] Code to determine if the iterable is an array or array-like.
* @param {boolean} [options.useHas] Specify using `hasOwnProperty` checks in the object loop.
* @param {Function} [options.keys] A reference to `_.keys` for use in own property iteration.
* @param {Object} [options] The compile options object.
* @param {string} [options.args] A comma separated string of iteration function arguments.
* @param {string} [options.top] Code to execute before the iteration branches.
* @param {string} [options.loop] Code to execute in the object loop.
* @param {string} [options.bottom] Code to execute after the iteration branches.
* @param {boolean} [options.useHas] Specify using `hasOwnProperty` checks in the object loop.
* @returns {Function} Returns the compiled function.
*/
function createIterator() {
// data properties
iteratorData.shadowedProps = shadowedProps;
// iterator options
iteratorData.array = iteratorData.bottom = iteratorData.loop = iteratorData.top = '';
iteratorData.init = 'iterable';
iteratorData.useHas = true;
// merge options into a template data object
for (var object, index = 0; object = arguments[index]; index++) {
for (var key in object) {
iteratorData[key] = object[key];
}
}
var args = iteratorData.args;
iteratorData.firstArg = /^[^,]+/.exec(args)[0];
function createIterator(options) {
options.shadowedProps = shadowedProps;
// create the function factory
var factory = Function(
'baseCreateCallback, errorClass, errorProto, hasOwnProperty, ' +
'indicatorObject, isArguments, isArray, isString, keys, objectProto, ' +
'objectTypes, nonEnumProps, stringClass, stringProto, toString',
'return function(' + args + ') {\n' + iteratorTemplate(iteratorData) + '\n}'
'baseCreateCallback, errorClass, errorProto, hasOwnProperty, isArguments, ' +
'objectProto, objectTypes, nonEnumProps, stringClass, stringProto, toString',
'return function(' + options.args + ') {\n' + iteratorTemplate(options) + '\n}'
);
// return the compiled function
return factory(
baseCreateCallback, errorClass, errorProto, hasOwnProperty,
indicatorObject, isArguments, isArray, isString, iteratorData.keys, objectProto,
objectTypes, nonEnumProps, stringClass, stringProto, toString
baseCreateCallback, errorClass, errorProto, hasOwnProperty, isArguments,
objectProto, objectTypes, nonEnumProps, stringClass, stringProto, toString
);
}
@@ -1948,7 +1914,8 @@
'args': 'object',
'init': '[]',
'top': 'if (!(objectTypes[typeof object])) return result',
'loop': 'result.push(index)'
'loop': 'result.push(key)',
'useHas': true
});
/**
@@ -1975,15 +1942,6 @@
return nativeKeys(object);
};
/** Reusable iterator options shared by `each`, `forIn`, and `forOwn` */
var eachIteratorOptions = {
'args': 'collection, callback, thisArg',
'top': "callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3)",
'array': "typeof length == 'number'",
'keys': keys,
'loop': 'if (callback(iterable[index], index, collection) === false) return result'
};
/**
* Used to convert characters to HTML entities:
*
@@ -2007,22 +1965,6 @@
var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'),
reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g');
/**
* A function compiled to iterate `arguments` objects, arrays, objects, and
* strings consistenly across environments, executing the callback for each
* element in the collection. The callback is bound to `thisArg` and invoked
* with three arguments; (value, index|key, collection). Callbacks may exit
* iteration early by explicitly returning `false`.
*
* @private
* @type Function
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Array|Object|string} Returns `collection`.
*/
var baseEach = createIterator(eachIteratorOptions);
/*--------------------------------------------------------------------------*/
/**
@@ -2034,7 +1976,6 @@
*
* @static
* @memberOf _
* @type Function
* @alias extend
* @category Objects
* @param {Object} object The destination object.
@@ -2220,7 +2161,6 @@
*
* @static
* @memberOf _
* @type Function
* @category Objects
* @param {Object} object The destination object.
* @param {...Object} [source] The source objects.
@@ -2393,9 +2333,11 @@
* });
* // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
*/
var forIn = createIterator(eachIteratorOptions, {
'top': 'if (!objectTypes[typeof iterable]) return result;\n' + eachIteratorOptions.top,
'array': false,
var forIn = createIterator({
'args': 'object, callback, thisArg',
'init': 'object',
'top': "callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3)",
'loop': 'if (callback(object[key], key, object) === false) return result',
'useHas': false
});
@@ -2452,7 +2394,6 @@
*
* @static
* @memberOf _
* @type Function
* @category Objects
* @param {Object} object The object to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
@@ -3751,7 +3692,7 @@
} else if (support.unindexedChars && isString(collection)) {
iterable = collection.split('');
}
baseEach(collection, function(value, key, collection) {
baseEach(iterable, function(value, key) {
key = props ? props[--length] : --length;
return callback(iterable[key], key, collection);
});