3

I know just about everything is considered an object in JavaScript. I am making a function which specifically requires the argument it accepts to be in this format:

{
   a: 'b',
   c: 'd'
}

So this type of key value pair inside curly braces sort of object not the other type of objects. How to validate for this specifically?

Phil
  • 157,677
  • 23
  • 242
  • 245
  • 3
    This has been answered many many times: `{}.toString.call(obj) == "[object Object]"`. – elclanrs Apr 05 '13 at 03:13
  • possible duplicate of [How do I get the name of an object's type in JavaScript?](http://stackoverflow.com/questions/332422/how-do-i-get-the-name-of-an-objects-type-in-javascript) – bfavaretto Apr 05 '13 at 03:22
  • @elclanrs For some reason, the Chrome console doesn't like the dot in `{}.toString` – Phil Apr 05 '13 at 03:22
  • 1
    Try: `({}).toString` or `Object.prototype.toString`. – elclanrs Apr 05 '13 at 03:23

6 Answers6

2

Update: After writing all of the below and later giving it further thought, I'd like to suggest that you take another look at Trevor's answer. I think it is more deserving of that beautiful green checkmark than my answer, and here's why.

I took your question at face value and ran with it: If you really want to do literally what you said, a function like $.isPlainObject() is the way to go. But is that really what you need to do? Keep in mind that $.isPlainObject() is not a very efficient function; it enumerates through all your object's properties. And as I noted below, it can't distinguish between {} and new Object().

Instead of all that, Trevor suggests simply checking for the properties you need, and for most cases, most of the time, I think he's right. It's definitely closer to the approach I take in my own code: validate what I need, ignore the rest.

So do take another look at his answer and carefully consider it. (And I won't be offended if you move the checkmark!)

Now my original answer:

There's a $.isPlainObject() function in jQuery. If you're not using jQuery, you can copy the source code for this function into your app. Open the jQuery source and search for isPlainObject:.

It's worth reading the source for this function in any case, to see that it isn't as simple as an typeof or instanceOf check:

isPlainObject: function( obj ) {
    // Must be an Object.
    // Because of IE, we also have to check the presence of the constructor property.
    // Make sure that DOM nodes and window objects don't pass through, as well
    if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
        return false;
    }

    try {
        // Not own constructor property must be Object
        if ( obj.constructor &&
            !core_hasOwn.call(obj, "constructor") &&
            !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
            return false;
        }
    } catch ( e ) {
        // IE8,9 Will throw exceptions on certain host objects #9897
        return false;
    }

    // Own properties are enumerated firstly, so to speed up,
    // if last one is own, then all properties are own.

    var key;
    for ( key in obj ) {}

    return key === undefined || core_hasOwn.call( obj, key );
},

type: function( obj ) {
    if ( obj == null ) {
        return String( obj );
    }
    return typeof obj === "object" || typeof obj === "function" ?
        class2type[ core_toString.call(obj) ] || "object" :
        typeof obj;
},

isWindow: function( obj ) {
    return obj != null && obj == obj.window;
},

And there is also the class2type object that this code uses:

// [[Class]] -> type pairs
class2type = {},

// Populate the class2type map
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
    class2type[ "[object " + name + "]" ] = name.toLowerCase();
});

(These code snippets are out of context; you'd need to tweak the syntax if you were using them standalone.)

Of course depending on the circumstances, you may not need all of these checks. And if you're using jQuery already, you can just call $.isPlainObject() and not sweat the details.

Also note that it is not possible to distinguish an object literal from an object created with new Object:

var obj1 = { a:'b', c:'d' };

var obj2 = new Object();
obj2.a = 'b';
obj2.c = 'd';

console.log( $.isPlainObject(obj1) );  // prints true
console.log( $.isPlainObject(obj2) );  // also prints true!

They both return true from $.isPlainObject(), and I'm pretty sure that any other test you devise will be unable to tell which is which either.

A funny historical note: When John Resig added this function to jQuery back in 2009, he was originally going to call it $.isObjectLiteral(). At my urging he changed the name to $.isPlainObject because of this ambiguity.

Community
  • 1
  • 1
Michael Geary
  • 28,450
  • 9
  • 65
  • 75
1

by using typeof

var obj = {x: 1, y: 2};
if (typeof obj == 'object') console.log('im an object');
Marc
  • 416
  • 3
  • 8
1

This checks whether value is an object and wether it has properties a and c:

var value = {a: 1, c: 2};
if (typeof value === "object" && "a" in value && "c" in value)
{
    ...
}
Igor
  • 15,833
  • 1
  • 27
  • 32
  • This good for checking that the properties you care about are there, because an array is also an object. `typeof [] === 'object'` – Trevor Dixon Apr 05 '13 at 03:19
  • 1
    `var value = [1,2]; value.a = 1; typeof value === "object" && "a" in value // true` – elclanrs Apr 05 '13 at 03:19
  • @elclanrs Haven't you just converted the array to an object though? – Phil Apr 05 '13 at 03:20
  • @Phil: It's still an array, it has all array methods available, try it. It just has a property without index. Not recommended to do that but proves this answer wrong. – elclanrs Apr 05 '13 at 03:21
  • "… specifically requires the argument it accepts to be in this format…" The OP doesn't *really* want an object that's absolutely not an array; he just wants something that has properties a and c. In my opinion, this is the best solution to the OP's *actual* problem. – Trevor Dixon Apr 05 '13 at 03:27
1

Pitfalls:

  • typeof of Arrays is 'object'

    typeof [] === 'object' //true
    
  • Array is an instance of Object

    Array instanceof Object //true
    
  • typeof null is 'object'

    typeof null === 'object' //true
    

You can do this which checks if:

  • arg is truthy. Since null is falsy, it's weeded here.
  • typeof arg is 'object'. AFAIK, only arrays and objects pass on this one.
  • finally, if arg is not instance of Array

    if(arg && typeof arg === 'object' && !(arg instanceof Array){...}
    
Joseph
  • 117,725
  • 30
  • 181
  • 234
1

In the situation you've described, I wouldn't worry about making sure it's not an array or some other kind of object. Just make sure the properties you need are there, and if they're missing, sound the alarm.

function needAandC(params) {
    if (params.a === undefined || params.c === undefined)
        throw new Error('Ahhh.');

    // You've got what you need, don't worry what else is there.
}
Trevor Dixon
  • 23,216
  • 12
  • 72
  • 109
  • I think you are right! In fact, I edited [my answer](http://stackoverflow.com/a/15825328/1202830) to recommend your approach instead unless the stricter validation is really needed. – Michael Geary Apr 05 '13 at 04:31
0

It also depends on what exactly you want to assert. As for most common case - parameter (or contract) validation, I recently noticed NPM team (https://github.com/npm/cli) relies on https://github.com/iarna/aproba function, which freakishly lightweight and simple:

validate("O", [ val ]); // if val is not object, throws an exception

What the function does:

var isObject = ( typeof val === 'object' && val !== null && !Array.isArray( val ) 
  &&  !( val instanceof Error ) && !isArguments( val ) );

function isArguments (thingy) {
  return thingy != null && typeof thingy === 'object' && thingy.hasOwnProperty('callee')
}
Dmitry Sheiko
  • 2,130
  • 1
  • 25
  • 28