diff --git a/src/core.js b/src/core.js index cae3a90e39..39e68c4fce 100644 --- a/src/core.js +++ b/src/core.js @@ -422,8 +422,11 @@ jQuery.extend({ }, isPlainObject: function( obj ) { + var key; // Not plain objects: // - Any object or value whose internal [[Class]] property is not "[object Object]" + // ...Unless it was a constructor, whose prototype property was paved over by + // by a plain object at its declaration. #13571 // - DOM nodes // - window if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { @@ -442,9 +445,11 @@ jQuery.extend({ return false; } - // If the function hasn't returned already, we're confident that - // |obj| is a plain object, created by {} or constructed with new Object - return true; + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); }, isEmptyObject: function( obj ) { diff --git a/test/unit/core.js b/test/unit/core.js index 7c840993b6..7936777111 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -296,9 +296,9 @@ test("type", function() { }); asyncTest("isPlainObject", function() { - expect(15); + expect(16); - var pass, iframe, doc, + var pass, iframe, doc, c, extended, fn = function() {}; // The use case that we want to match @@ -360,6 +360,45 @@ asyncTest("isPlainObject", function() { } catch(e) { window.iframeDone( Object, "iframes not supported" ); } + + // #13571 + function C() {} + C.prototype = { + x: 1 + }; + c = new C(); + + extended = jQuery.extend( true, {}, { + target: c + }); + + strictEqual( + extended.target, c, + "Instances, whose constructor defined its prototype by assigning a plain object, " + + "will lie about their true identity to preserve a broken user-code expectation" + ); + // + // The test above is broken and tests a broken feature, to support a misinformed + // assumption, as documented here: + // + // http://bugs.jquery.com/ticket/13571#comment:4 + // + // It will not pass if the object being assigned as the prototype + // has no properties: + // + // function C() {} + // C.prototype = {}; + // c = new C(); + + // extended = jQuery.extend( true, {}, { + // target: c + // }); + + // strictEqual( extended.target, c, "Undetectable, will fail every time" ); + // + // The solution is to reset the constructor property of your plain object prototypes. + // + // }); test("isFunction", function() {