diff options
author | Richard Gibson <richard.gibson@gmail.com> | 2016-03-14 16:46:51 -0400 |
---|---|---|
committer | Timmy Willison <timmywillisn@gmail.com> | 2016-04-04 12:02:13 -0400 |
commit | e0d3bfa77073a245ca112736a1ed3db07d5adcf6 (patch) | |
tree | 4226878c88c40068e2eea1659339ada15965979c | |
parent | 10fc59007d717432ea126e49ce4142e6c4d5136e (diff) | |
download | jquery-e0d3bfa77073a245ca112736a1ed3db07d5adcf6.tar.gz jquery-e0d3bfa77073a245ca112736a1ed3db07d5adcf6.zip |
Core: Simplify isPlainObject
Fixes gh-2986
Close gh-2998
-rw-r--r-- | src/core.js | 36 | ||||
-rw-r--r-- | src/var/ObjectFunctionString.js | 5 | ||||
-rw-r--r-- | src/var/fnToString.js | 5 | ||||
-rw-r--r-- | src/var/getProto.js | 3 | ||||
-rw-r--r-- | test/unit/core.js | 11 |
5 files changed, 37 insertions, 23 deletions
diff --git a/src/core.js b/src/core.js index a9898e51d..7c2433529 100644 --- a/src/core.js +++ b/src/core.js @@ -1,6 +1,7 @@ define( [ "./var/arr", "./var/document", + "./var/getProto", "./var/slice", "./var/concat", "./var/push", @@ -8,10 +9,13 @@ define( [ "./var/class2type", "./var/toString", "./var/hasOwn", + "./var/fnToString", + "./var/ObjectFunctionString", "./var/support", "./core/DOMEval" -], function( arr, document, slice, concat, - push, indexOf, class2type, toString, hasOwn, support, DOMEval ) { +], function( arr, document, getProto, slice, concat, push, indexOf, + class2type, toString, hasOwn, fnToString, ObjectFunctionString, + support, DOMEval ) { var version = "@VERSION", @@ -225,28 +229,24 @@ jQuery.extend( { }, isPlainObject: function( obj ) { - var key; + var proto, Ctor; - // Not plain objects: - // - Any object or value whose internal [[Class]] property is not "[object Object]" - // - DOM nodes - // - window - if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { return false; } - // Not own constructor property must be Object - if ( obj.constructor && - !hasOwn.call( obj, "constructor" ) && - !hasOwn.call( obj.constructor.prototype || {}, "isPrototypeOf" ) ) { - return false; - } + proto = getProto( obj ); - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own - for ( key in obj ) {} + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } - return key === undefined || hasOwn.call( obj, key ); + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; }, isEmptyObject: function( obj ) { diff --git a/src/var/ObjectFunctionString.js b/src/var/ObjectFunctionString.js new file mode 100644 index 000000000..570197908 --- /dev/null +++ b/src/var/ObjectFunctionString.js @@ -0,0 +1,5 @@ +define( [ + "./fnToString" +], function( fnToString ) { + return fnToString.call( Object ); +} ); diff --git a/src/var/fnToString.js b/src/var/fnToString.js new file mode 100644 index 000000000..a92e6ebb4 --- /dev/null +++ b/src/var/fnToString.js @@ -0,0 +1,5 @@ +define( [ + "./hasOwn" +], function( hasOwn ) { + return hasOwn.toString; +} ); diff --git a/src/var/getProto.js b/src/var/getProto.js new file mode 100644 index 000000000..dccf3040a --- /dev/null +++ b/src/var/getProto.js @@ -0,0 +1,3 @@ +define( function() { + return Object.getPrototypeOf; +} ); diff --git a/test/unit/core.js b/test/unit/core.js index 5bf3ab1d1..f18ad2e82 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -287,7 +287,7 @@ QUnit.test( "type for `Symbol`", function( assert ) { QUnit.asyncTest( "isPlainObject", function( assert ) { - assert.expect( 22 ); + assert.expect( 23 ); var pass, iframe, doc, parentObj, childObj, deep, fn = function() {}; @@ -300,12 +300,13 @@ QUnit.asyncTest( "isPlainObject", function( assert ) { assert.ok( jQuery.isPlainObject( { constructor: "foo" } ), "plain object with primitive constructor property" ); - parentObj = { foo: "bar" }; + parentObj = {}; childObj = Object.create( parentObj ); - - assert.ok( !jQuery.isPlainObject( childObj ), "isPlainObject(Object.create({}))" ); + assert.ok( !jQuery.isPlainObject( childObj ), "Object.create({})" ); + parentObj.foo = "bar"; + assert.ok( !jQuery.isPlainObject( childObj ), "Object.create({...})" ); childObj.bar = "foo"; - assert.ok( !jQuery.isPlainObject( childObj ), "isPlainObject(Object.create({}))" ); + assert.ok( !jQuery.isPlainObject( childObj ), "extend(Object.create({...}), ...)" ); // Not objects shouldn't be matched assert.ok( !jQuery.isPlainObject( "" ), "string" ); |