diff --git a/lodash.js b/lodash.js index 8e216fc1e..8ff413c50 100644 --- a/lodash.js +++ b/lodash.js @@ -6572,8 +6572,11 @@ * // => ['x', 'y'] (property order is not guaranteed across environments) */ var keys = !nativeKeys ? shimKeys : function(object) { - var length = object ? object.length : 0; - if (typeof length == 'number' && length > 0) { + var ctor = object && object.constructor, + length = object ? object.length : 0; + + if (typeof length == 'number' && length > 0 || + (ctor && object === ctor.prototype)) { return shimKeys(object); } return isObject(object) ? nativeKeys(object) : []; @@ -6609,7 +6612,9 @@ (support.nonEnumArgs && isArguments(object))) && length) >>> 0; var keyIndex, + ctor = object.constructor, index = -1, + isProto = ctor && object === ctor.prototype, maxIndex = length - 1, result = Array(length), skipIndexes = length > 0, @@ -6620,7 +6625,8 @@ result[index] = String(index); } for (var key in object) { - if (!(skipProto && key == 'prototype') && + if (!(isProto && key == 'constructor') && + !(skipProto && key == 'prototype') && !(skipErrorProps && (key == 'message' || key == 'name')) && !(skipIndexes && (keyIndex = +key, keyIndex > -1 && keyIndex <= maxIndex && keyIndex % 1 == 0))) { result.push(key); @@ -6631,11 +6637,10 @@ // attribute of an existing property and the `constructor` property of a // prototype defaults to non-enumerable. if (support.nonEnumShadows && object !== objectProto) { - var ctor = object.constructor; index = -1; length = shadowedProps.length; - if (object === (ctor && ctor.prototype)) { + if (isProto) { var className = object === stringProto ? stringClass : object === errorProto ? errorClass : toString.call(object), nonEnum = nonEnumProps[className]; } diff --git a/test/test.js b/test/test.js index 58d7cce1f..ba96e7113 100644 --- a/test/test.js +++ b/test/test.js @@ -5493,23 +5493,16 @@ delete String.prototype.a; }); - test('`_.' + methodName + '` fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 2, function() { - function Foo() {} - Foo.prototype.a = 1; - - var actual = func(Foo.prototype); - deepEqual(actual, ['a']); - - actual = func(shadowedObject); + test('`_.' + methodName + '` fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() { + var actual = func(shadowedObject); deepEqual(actual.sort(), shadowedProps); }); 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.c = 3; - Foo.a = 1; Foo.b = 2; + Foo.prototype.c = 3; var expected = ['a', 'b'], actual = func(Foo); @@ -5518,10 +5511,20 @@ Foo.prototype = { 'c': 3 }; actual = func(Foo); - deepEqual(actual.sort(), expected); }); + test('`_.' + methodName + '` skips the `constructor` property on prototype objects', 2, function() { + function Foo() {} + Foo.prototype.a = 1; + + var expected = ['a']; + deepEqual(func(Foo.prototype), ['a']); + + Foo.prototype = { 'constructor': Foo, 'a': 1 }; + deepEqual(func(Foo.prototype), ['a']); + }); + test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties', 1, function() { function Foo() { this.a = 1;