diff --git a/lodash.js b/lodash.js index ba1c886b8..1fc95668b 100644 --- a/lodash.js +++ b/lodash.js @@ -8049,7 +8049,7 @@ } if (support.nonEnumShadows && object !== objectProto) { var className = object === stringProto ? stringClass : object === errorProto ? errorClass : toString.call(object), - nonEnum = nonEnumProps[className] || nonEnumProps[objectClass]; + nonEnums = nonEnumProps[className] || nonEnumProps[objectClass]; if (className == objectClass) { proto = objectProto; @@ -8057,8 +8057,9 @@ length = shadowedProps.length; while (length--) { key = shadowedProps[length]; - if (!(isProto && nonEnum[key]) && - (key == 'constructor' ? hasOwnProperty.call(object, key) : object[key] !== proto[key])) { + var nonEnum = nonEnums[key]; + if (!(isProto && nonEnum) && + (nonEnum ? hasOwnProperty.call(object, key) : object[key] !== proto[key])) { result.push(key); } } diff --git a/test/test.js b/test/test.js index 1b2cd89fe..31feb83f9 100644 --- a/test/test.js +++ b/test/test.js @@ -4535,36 +4535,6 @@ deepEqual(props.sort(), shadowedProps); }); - test('`_.' + methodName + '` does not iterate over non-enumerable properties (test in IE < 9)', 10, function() { - _.forOwn({ - 'Array': arrayProto, - 'Boolean': Boolean.prototype, - 'Date': Date.prototype, - 'Error': errorProto, - 'Function': funcProto, - 'Object': objectProto, - 'Number': Number.prototype, - 'TypeError': TypeError.prototype, - 'RegExp': RegExp.prototype, - 'String': stringProto - }, - function(proto, key) { - var message = 'non-enumerable properties on ' + key + '.prototype', - props = []; - - func(proto, function(value, prop) { props.push(prop); }); - - if (/Error/.test(key)) { - ok(_.every(['constructor', 'toString'], function(prop) { - return !_.contains(props, prop); - }), message); - } - else { - deepEqual(props, [], message); - } - }); - }); - test('`_.' + methodName + '` skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() { function Foo() {} Foo.prototype.a = 1; @@ -7094,6 +7064,47 @@ deepEqual(func(new Foo).sort(), actual); }); + test('`_.' + methodName + '` skips non-enumerable properties (test in IE < 9)', 50, function() { + _.forOwn({ + 'Array': arrayProto, + 'Boolean': Boolean.prototype, + 'Date': Date.prototype, + 'Error': errorProto, + 'Function': funcProto, + 'Object': objectProto, + 'Number': Number.prototype, + 'TypeError': TypeError.prototype, + 'RegExp': RegExp.prototype, + 'String': stringProto + }, + function(proto, key) { + _.each([proto, _.create(proto)], function(object, index) { + var actual = func(proto), + isErr = /Error/.test(key), + message = 'enumerable properties ' + (index ? 'inherited from' : 'on') + ' `' + key + '.prototype`', + props = isErr ? ['constructor', 'toString'] : ['constructor']; + + actual = isErr ? _.difference(props, actual) : actual; + strictEqual(_.isEmpty(actual), !isErr, 'skips non-' + message); + + proto.a = 1; + actual = func(object); + delete proto.a; + + strictEqual(_.contains(actual, 'a'), !(isKeys && index), 'includes ' + message); + + if (index) { + object.constructor = 1; + if (isErr) { + object.toString = 2; + } + actual = func(object); + ok(_.isEmpty(_.difference(props, actual)), 'includes shadowed properties on objects that inherit from `' + key + '.prototype`'); + } + }); + }); + }); + test('`_.' + methodName + '` skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() { function Foo() {} Foo.a = 1;