Ensure _.keys and _.keysIn skip "length" keys for strict mode arguments objects in Safari 9.

This commit is contained in:
John-David Dalton
2015-11-13 19:01:33 -08:00
parent c71d26c7e8
commit 59ec97f59f
2 changed files with 46 additions and 27 deletions

View File

@@ -4764,20 +4764,18 @@
} }
/** /**
* Initializes an array of property names based on `object`. If `object` is * Creates an array of index keys for `object` values of arrays,
* an array, `arguments` object, or `string` its index keys are returned, * `arguments` objects, and strings, otherwise `null` is returned.
* otherwise an empty array is returned.
* *
* @private * @private
* @param {Object} object The object to query. * @param {Object} object The object to query.
* @returns {Array} Returns the initialized array of property names. * @returns {Array|null} Returns index keys, else `null`.
*/ */
function initKeys(object) { function indexKeys(object) {
var length = object ? object.length : 0; var length = object ? object.length : undefined;
length = (length && isLength(length) && return (isLength(length) && (isArray(object) || isString(object) || isArguments(object)))
(isArray(object) || isString(object) || isArguments(object)) && length) || 0; ? baseTimes(length, String)
: null;
return baseTimes(length, String);
} }
/** /**
@@ -10808,13 +10806,14 @@
if (!(isProto || isArrayLike(object))) { if (!(isProto || isArrayLike(object))) {
return baseKeys(object); return baseKeys(object);
} }
var result = initKeys(object), var indexes = indexKeys(object),
length = result.length, skipIndexes = !!indexes,
skipIndexes = !!length; result = indexes || [],
length = result.length;
for (var key in object) { for (var key in object) {
if (baseHas(object, key) && if (baseHas(object, key) &&
!(skipIndexes && isIndex(key, length)) && !(skipIndexes && (key == 'length' || isIndex(key, length))) &&
!(isProto && key == 'constructor')) { !(isProto && key == 'constructor')) {
result.push(key); result.push(key);
} }
@@ -10849,13 +10848,14 @@
isProto = isPrototype(object), isProto = isPrototype(object),
props = baseKeysIn(object), props = baseKeysIn(object),
propsLength = props.length, propsLength = props.length,
result = initKeys(object), indexes = indexKeys(object),
length = result.length, skipIndexes = !!indexes,
skipIndexes = !!length; result = indexes || [],
length = result.length;
while (++index < propsLength) { while (++index < propsLength) {
var key = props[index]; var key = props[index];
if (!(skipIndexes && isIndex(key, length)) && if (!(skipIndexes && (key == 'length' || isIndex(key, length))) &&
!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
result.push(key); result.push(key);
} }

View File

@@ -10678,7 +10678,8 @@
QUnit.module('keys methods'); QUnit.module('keys methods');
lodashStable.each(['keys', 'keysIn'], function(methodName) { lodashStable.each(['keys', 'keysIn'], function(methodName) {
var args = arguments, var args = (function() { return arguments; }(1, 2, 3)),
strictArgs = (function() { 'use strict'; return arguments; }(1, 2, 3)),
func = _[methodName], func = _[methodName],
isKeys = methodName == 'keys'; isKeys = methodName == 'keys';
@@ -10740,25 +10741,43 @@
QUnit.test('`_.' + methodName + '` should work with `arguments` objects', function(assert) { QUnit.test('`_.' + methodName + '` should work with `arguments` objects', function(assert) {
assert.expect(1); assert.expect(1);
assert.deepEqual(func(args).sort(), ['0', '1', '2']); var values = [args, strictArgs],
expected = lodashStable.map(values, lodashStable.constant(['0', '1', '2'])),
actual = lodashStable.map(values, func);
assert.deepEqual(actual, expected);
}); });
QUnit.test('`_.' + methodName + '` should return keys for custom properties on `arguments` objects', function(assert) { QUnit.test('`_.' + methodName + '` should return keys for custom properties on `arguments` objects', function(assert) {
assert.expect(1); assert.expect(1);
args.a = 1; var values = [args, strictArgs],
assert.deepEqual(func(args).sort(), ['0', '1', '2', 'a']); expected = lodashStable.map(values, lodashStable.constant(['0', '1', '2', 'a']));
delete args.a;
var actual = lodashStable.map(values, function(value) {
value.a = 1;
var result = func(value).sort();
delete value.a;
return result;
});
assert.deepEqual(actual, expected);
}); });
QUnit.test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties of `arguments` objects', function(assert) { QUnit.test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties of `arguments` objects', function(assert) {
assert.expect(1); assert.expect(1);
var expected = isKeys ? ['0', '1', '2'] : ['0', '1', '2', 'a']; var values = [args, strictArgs],
expected = lodashStable.map(values, lodashStable.constant(isKeys ? ['0', '1', '2'] : ['0', '1', '2', 'a']));
objectProto.a = 1; var actual = lodashStable.map(values, function(value) {
assert.deepEqual(func(args).sort(), expected); objectProto.a = 1;
delete objectProto.a; var result = func(value).sort();
delete objectProto.a;
return result;
});
assert.deepEqual(actual, expected);
}); });
QUnit.test('`_.' + methodName + '` should work with string objects', function(assert) { QUnit.test('`_.' + methodName + '` should work with string objects', function(assert) {