aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Gibson <richard.gibson@gmail.com>2016-03-14 16:46:51 -0400
committerTimmy Willison <timmywillisn@gmail.com>2016-04-04 12:02:13 -0400
commite0d3bfa77073a245ca112736a1ed3db07d5adcf6 (patch)
tree4226878c88c40068e2eea1659339ada15965979c
parent10fc59007d717432ea126e49ce4142e6c4d5136e (diff)
downloadjquery-e0d3bfa77073a245ca112736a1ed3db07d5adcf6.tar.gz
jquery-e0d3bfa77073a245ca112736a1ed3db07d5adcf6.zip
Core: Simplify isPlainObject
Fixes gh-2986 Close gh-2998
-rw-r--r--src/core.js36
-rw-r--r--src/var/ObjectFunctionString.js5
-rw-r--r--src/var/fnToString.js5
-rw-r--r--src/var/getProto.js3
-rw-r--r--test/unit/core.js11
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" );