From 0beaf47a6444f27a821b29cf9946b4752691a16b Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Tue, 14 Jul 2015 22:46:17 -0700 Subject: [PATCH] Extract common components of `_.keys` and `_.keysIn` and make `_.keysIn` use the `Reflect.enumerate` shim as a compat path for older enviros. --- lodash.src.js | 167 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 115 insertions(+), 52 deletions(-) diff --git a/lodash.src.js b/lodash.src.js index e02136c14..cbc7dfe98 100644 --- a/lodash.src.js +++ b/lodash.src.js @@ -142,9 +142,9 @@ var contextProps = [ 'Array', 'ArrayBuffer', 'Date', 'Error', 'Float32Array', 'Float64Array', 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Math', 'Number', - 'Object', 'RegExp', 'Set', 'String', '_', 'clearTimeout', 'isFinite', - 'parseFloat', 'parseInt', 'setTimeout', 'TypeError', 'Uint8Array', - 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap' + 'Object', 'Reflect', 'RegExp', 'Set', 'String', 'TypeError', 'Uint8Array', + 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', '_', + 'clearTimeout', 'isFinite', 'parseFloat', 'parseInt', 'setTimeout', ]; /** Used to make template sourceURLs easier to identify. */ @@ -743,6 +743,7 @@ Math = context.Math, Number = context.Number, Object = context.Object, + Reflect = context.Reflect, RegExp = context.RegExp, String = context.String, TypeError = context.TypeError; @@ -782,6 +783,7 @@ /** Native method references. */ var ArrayBuffer = context.ArrayBuffer, clearTimeout = context.clearTimeout, + enumerate = Reflect ? Reflect.enumerate : undefined, getPrototypeOf = Object.getPrototypeOf, parseFloat = context.parseFloat, pow = Math.pow, @@ -1862,29 +1864,6 @@ return result; } - /** - * The base implementation of `_.iteratee`. - * - * @private - * @param {*} [func=_.identity] The value to convert to an iteratee. - * @returns {Function} Returns the iteratee. - */ - function baseIteratee(func) { - var type = typeof func; - if (type == 'function') { - return func; - } - if (func == null) { - return identity; - } - if (type == 'object') { - return isArray(func) - ? baseMatchesProperty(func[0], func[1]) - : baseMatches(func); - } - return property(func); - } - /** * The base implementation of `baseForIn` and `baseForOwn` which iterates * over `object` properties returned by `keysFunc` invoking `iteratee` for @@ -2145,6 +2124,59 @@ return true; } + /** + * The base implementation of `_.iteratee`. + * + * @private + * @param {*} [func=_.identity] The value to convert to an iteratee. + * @returns {Function} Returns the iteratee. + */ + function baseIteratee(func) { + var type = typeof func; + if (type == 'function') { + return func; + } + if (func == null) { + return identity; + } + if (type == 'object') { + return isArray(func) + ? baseMatchesProperty(func[0], func[1]) + : baseMatches(func); + } + return property(func); + } + + /** + * The base implementation of `_.keysIn` which does not skip the constructor + * property of prototypes or treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeysIn(object) { + var result = []; + for (var key in object) { + result.push(key); + } + return result; + } + + // An alternative implementation intended for IE < 9 with es6-shim. + if (enumerate && !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf')) { + var baseKeysIn = function(object) { + var data, + iterator = enumerate(object), + result = []; + + while (!(data = iterator.next()).done) { + result.push(data.value); + } + return result; + }; + } + /** * The base implementation of `_.map` without support for callback shorthands. * @@ -3768,6 +3800,29 @@ return result; } + /** + * Initializes an array of property names based on `object`. If `object` is + * an array, `arguments` object, or `string` its index keys are returned, + * otherwise an empty array is returned. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the initialized array of property names. + */ + function initKeys(object) { + var length = object.length; + length = (length && isLength(length) && + (isArray(object) || isArguments(object) || isString(object)) && length) || 0; + + var index = -1, + result = Array(length); + + while (++index < length) { + result[index] = (index + ''); + } + return result; + } + /** * Invokes the method at `path` on `object`. * @@ -3889,6 +3944,20 @@ return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; } + /** + * Checks if `value` is a prototype. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. + */ + function isPrototype(value) { + var Ctor = !!value && value.constructor, + proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; + + return value === proto; + } + /** * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. * @@ -8981,23 +9050,19 @@ */ function keys(object) { object = toObject(object); - if (!isArrayLike(object)) { + + var isProto = isPrototype(object); + if (!(isProto || isArrayLike(object))) { return nativeKeys(object); } - var length = object.length; - length = (length && isLength(length) && - (isArray(object) || isArguments(object) || isString(object)) && length) || 0; + var result = initKeys(object), + length = result.length, + skipIndexes = !!length; - var index = -1, - skipIndexes = length > 0, - result = Array(length); - - while (++index < length) { - result[index] = (index + ''); - } for (var key in object) { if (hasOwnProperty.call(object, key) && - !(skipIndexes && isIndex(key, length))) { + !(skipIndexes && isIndex(key, length)) && + !(isProto && key == 'constructor')) { result.push(key); } } @@ -9029,22 +9094,20 @@ function keysIn(object) { object = toObject(object); - var cache = {}, - result = []; + var index = -1, + isProto = isPrototype(object), + props = baseKeysIn(object), + propsLength = props.length, + result = initKeys(object), + length = result.length, + skipIndexes = !!length; - while (object) { - var index = -1, - props = keys(object), - length = props.length; - - while (++index < length) { - var key = props[index]; - if (cache[key] !== cache) { - result.push(key); - cache[key] = cache; - } + while (++index < propsLength) { + var key = props[index]; + if (!(skipIndexes && isIndex(key, length)) && + !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { + result.push(key); } - object = getPrototypeOf(object); } return result; }