How to determine if an object is an object literal in Javascript?
Asked Answered
A

11

27

Is there any way to determine in Javascript if an object was created using object-literal notation or using a constructor method?

It seems to me that you just access it's parent object, but if the object you are passing in doesn't have a reference to it's parent, I don't think you can tell this, can you?

Ansate answered 23/7, 2009 at 18:15 Comment(4)
jeresig asked me to implement a function that would do just that for him.Ansate
An object is never an object literal, so can you rephrase the question?Electrostatic
And by the way, what was your favorite Commodore 64 magazine?Electrostatic
@Electrostatic I think I was too young for that at the time, I got my first C64 when I was 3 years old. All I knew was the whole: load "*", 8, 1 thing so I could load my games at that point.Ansate
A
13

I just came across this question and thread during a sweet hackfest that involved a grail quest for evaluating whether an object was created with {} or new Object() (i still havent figured that out.)

Anyway, I was suprised to find the similarity between the isObjectLiteral() function posted here and my own isObjLiteral() function that I wrote for the Pollen.JS project. I believe this solution was posted prior to my Pollen.JS commit, so - hats off to you! The upside to mine is the length... less then half (when included your set up routine), but both produce the same results.

Take a look:

function isObjLiteral(_obj) {
  var _test  = _obj;
  return (  typeof _obj !== 'object' || _obj === null ?
              false :  
              (
                (function () {
                  while (!false) {
                    if (  Object.getPrototypeOf( _test = Object.getPrototypeOf(_test)  ) === null) {
                      break;
                    }      
                  }
                  return Object.getPrototypeOf(_obj) === _test;
                })()
              )
          );
}

Additionally, some test stuff:

var _cases= {
    _objLit : {}, 
    _objNew : new Object(),
    _function : new Function(),
    _array : new Array(), 
    _string : new String(),
    _image : new Image(),
    _bool: true
};

console.dir(_cases);

for ( var _test in _cases ) {
  console.group(_test);
  console.dir( {
    type:    typeof _cases[_test], 
    string:  _cases[_test].toString(), 
    result:  isObjLiteral(_cases[_test])  
  });    
  console.groupEnd();
}

Or on jsbin.com...

http://jsbin.com/iwuwa

Be sure to open firebug when you get there - debugging to the document is for IE lovers.

Adequacy answered 23/7, 2009 at 18:15 Comment(9)
@Rick, this solution seems more elegant.Ansate
@leeand00: It's about the same length as mine. I just included an implementation of Object.getPrototypeOf in mine too, which this one doesn't include.Holtz
Also, if you remove the Object.getPrototypeOf.isNative part, mine is only 6 SLOC.Holtz
This doesn't work in IE 8 since Object.getPrototypeOf doesn't exist. Thus the shorter length. It also runs forever with some implementations of getPrototypeOf, such as John Resig's, which can return undefined in special casesCasanova
mhm...can you explain me why you wrote !false instead of true in the while loop condition?Forgetful
I'm seeing this break when undefined or null is passed in.Chromo
why not just Object.getPrototypeOf(Object.getPrototypeOf(_obj))===nullChristogram
Fails with isObjLiteral( JSON ) // returns trueConsume
Uhh, it does not work with Objects which have prototypes of prototypes: isObjLiteral(Object.create(Object.create({cool: "joes"})))Kremlin
M
23

What you want is:

Object.getPrototypeOf(obj) === Object.prototype

This checks that the object is a plain object created with either new Object() or {...} and not some subclass of Object.

Mulvey answered 4/5, 2017 at 4:24 Comment(1)
Nice and simple. I like it. But remember this won't work if obj was instantiated in a different window or frame. For that see Eli Grey's answer below. Even then keep in mind that you'll still get false positives and negatives depending how obj is instantiated. Ask yourself why you need to know if an object was instantiated with an object literal in the first place. Because if you just need to rule out that it isn't something else like a FormData object for example, then you can check easily with Object.prototype.toString.call(obj) which will return "[object FormData]"Undercoat
A
13

I just came across this question and thread during a sweet hackfest that involved a grail quest for evaluating whether an object was created with {} or new Object() (i still havent figured that out.)

Anyway, I was suprised to find the similarity between the isObjectLiteral() function posted here and my own isObjLiteral() function that I wrote for the Pollen.JS project. I believe this solution was posted prior to my Pollen.JS commit, so - hats off to you! The upside to mine is the length... less then half (when included your set up routine), but both produce the same results.

Take a look:

function isObjLiteral(_obj) {
  var _test  = _obj;
  return (  typeof _obj !== 'object' || _obj === null ?
              false :  
              (
                (function () {
                  while (!false) {
                    if (  Object.getPrototypeOf( _test = Object.getPrototypeOf(_test)  ) === null) {
                      break;
                    }      
                  }
                  return Object.getPrototypeOf(_obj) === _test;
                })()
              )
          );
}

Additionally, some test stuff:

var _cases= {
    _objLit : {}, 
    _objNew : new Object(),
    _function : new Function(),
    _array : new Array(), 
    _string : new String(),
    _image : new Image(),
    _bool: true
};

console.dir(_cases);

for ( var _test in _cases ) {
  console.group(_test);
  console.dir( {
    type:    typeof _cases[_test], 
    string:  _cases[_test].toString(), 
    result:  isObjLiteral(_cases[_test])  
  });    
  console.groupEnd();
}

Or on jsbin.com...

http://jsbin.com/iwuwa

Be sure to open firebug when you get there - debugging to the document is for IE lovers.

Adequacy answered 23/7, 2009 at 18:15 Comment(9)
@Rick, this solution seems more elegant.Ansate
@leeand00: It's about the same length as mine. I just included an implementation of Object.getPrototypeOf in mine too, which this one doesn't include.Holtz
Also, if you remove the Object.getPrototypeOf.isNative part, mine is only 6 SLOC.Holtz
This doesn't work in IE 8 since Object.getPrototypeOf doesn't exist. Thus the shorter length. It also runs forever with some implementations of getPrototypeOf, such as John Resig's, which can return undefined in special casesCasanova
mhm...can you explain me why you wrote !false instead of true in the while loop condition?Forgetful
I'm seeing this break when undefined or null is passed in.Chromo
why not just Object.getPrototypeOf(Object.getPrototypeOf(_obj))===nullChristogram
Fails with isObjLiteral( JSON ) // returns trueConsume
Uhh, it does not work with Objects which have prototypes of prototypes: isObjLiteral(Object.create(Object.create({cool: "joes"})))Kremlin
H
11

Edit: I'm interpreting "object literal" as anything created using an object literal or the Object constructor. This is what John Resig most likely meant.

I have a function that will work even if .constructor has been tainted or if the object was created in another frame. Note that Object.prototype.toString.call(obj) === "[object Object]" (as some may believe) will not solve this problem.

function isObjectLiteral(obj) {
    if (typeof obj !== "object" || obj === null)
        return false;

    var hasOwnProp = Object.prototype.hasOwnProperty,
    ObjProto = obj;

    // get obj's Object constructor's prototype
    while (Object.getPrototypeOf(ObjProto = Object.getPrototypeOf(ObjProto)) !== null);

    if (!Object.getPrototypeOf.isNative) // workaround if non-native Object.getPrototypeOf
        for (var prop in obj)
            if (!hasOwnProp.call(obj, prop) && !hasOwnProp.call(ObjProto, prop)) // inherited elsewhere
                return false;

    return Object.getPrototypeOf(obj) === ObjProto;
};


if (!Object.getPrototypeOf) {
    if (typeof ({}).__proto__ === "object") {
        Object.getPrototypeOf = function (obj) {
            return obj.__proto__;
        };
        Object.getPrototypeOf.isNative = true;
    } else {
        Object.getPrototypeOf = function (obj) {
            var constructor = obj.constructor,
            oldConstructor;
            if (Object.prototype.hasOwnProperty.call(obj, "constructor")) {
                oldConstructor = constructor;
                if (!(delete obj.constructor)) // reset constructor
                    return null; // can't delete obj.constructor, return null
                constructor = obj.constructor; // get real constructor
                obj.constructor = oldConstructor; // restore constructor
            }
            return constructor ? constructor.prototype : null; // needed for IE
        };
        Object.getPrototypeOf.isNative = false;
    }
} else Object.getPrototypeOf.isNative = true;

Here is the HTML for the testcase:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <!-- Online here: http://code.eligrey.com/testcases/all/isObjectLiteral.html -->
    <title>isObjectLiteral</title>
    <style type="text/css">
    li { background: green; } li.FAIL { background: red; }
    iframe { display: none; }
    </style>
</head>
<body>
<ul id="results"></ul>
<script type="text/javascript">
function isObjectLiteral(obj) {
    if (typeof obj !== "object" || obj === null)
        return false;

    var hasOwnProp = Object.prototype.hasOwnProperty,
    ObjProto = obj;

    // get obj's Object constructor's prototype
    while (Object.getPrototypeOf(ObjProto = Object.getPrototypeOf(ObjProto)) !== null);

    if (!Object.getPrototypeOf.isNative) // workaround if non-native Object.getPrototypeOf
        for (var prop in obj)
            if (!hasOwnProp.call(obj, prop) && !hasOwnProp.call(ObjProto, prop)) // inherited elsewhere
                return false;

    return Object.getPrototypeOf(obj) === ObjProto;
};


if (!Object.getPrototypeOf) {
    if (typeof ({}).__proto__ === "object") {
        Object.getPrototypeOf = function (obj) {
            return obj.__proto__;
        };
        Object.getPrototypeOf.isNative = true;
    } else {
        Object.getPrototypeOf = function (obj) {
            var constructor = obj.constructor,
            oldConstructor;
            if (Object.prototype.hasOwnProperty.call(obj, "constructor")) {
                oldConstructor = constructor;
                if (!(delete obj.constructor)) // reset constructor
                    return null; // can't delete obj.constructor, return null
                constructor = obj.constructor; // get real constructor
                obj.constructor = oldConstructor; // restore constructor
            }
            return constructor ? constructor.prototype : null; // needed for IE
        };
        Object.getPrototypeOf.isNative = false;
    }
} else Object.getPrototypeOf.isNative = true;

// Function serialization is not permitted
// Does not work across all browsers
Function.prototype.toString = function(){};

// The use case that we want to match
log("{}", {}, true);

// Instantiated objects shouldn't be matched
log("new Date", new Date, false);

var fn = function(){};

// Makes the function a little more realistic
// (and harder to detect, incidentally)
fn.prototype = {someMethod: function(){}};

// Functions shouldn't be matched
log("fn", fn, false);

// Again, instantiated objects shouldn't be matched
log("new fn", new fn, false);

var fn2 = function(){};

log("new fn2", new fn2, false);

var fn3 = function(){};

fn3.prototype = {}; // impossible to detect (?) without native Object.getPrototypeOf

log("new fn3 (only passes with native Object.getPrototypeOf)", new fn3, false);

log("null", null, false);

log("undefined", undefined, false);


/* Note:
 * The restriction against instantiated functions is
 * due to the fact that this method will be used for
 * deep-cloning an object. Instantiated objects will
 * just have their reference copied over, whereas
 * plain objects will need to be completely cloned.
 */

var iframe = document.createElement("iframe");
document.body.appendChild(iframe);

var doc = iframe.contentDocument || iframe.contentWindow.document;
doc.open();
doc.write("<body onload='window.top.iframeDone(Object);'>");
doc.close();

function iframeDone(otherObject){
    // Objects from other windows should be matched
    log("new otherObject", new otherObject, true);
}

function log(msg, a, b) {
  var pass = isObjectLiteral(a) === b ? "PASS" : "FAIL";

  document.getElementById("results").innerHTML +=
    "<li class='" + pass + "'>" + msg + "</li>";
}


</script>
</body>
</html>
Holtz answered 23/7, 2009 at 18:15 Comment(8)
Great answer, and it is nice to have you on stackoverflow. Off topic, but thanks also for the e4x array methods.Vernverna
Now that I've read your answer, I think I understand the question.Dabney
Whoa! what's the 3x equal sign about (===)?Ansate
@Ansate It means "exactly equal to". For example: var x = {valueOf:function(){return 1}}; (x == 1) === true; (x === 1) === true;Holtz
The second example was supposed to end with === false I meant.Holtz
Fails with Objects which have prototypes in the prototypes: isObjectLiteral(Object.create(Object.create({cool: "joes"})))Kremlin
It's hard to believe that === was ever not valid JavaScript but it's true (ES1 the forgotten version of JS)Undercoat
@Kremlin I don't see how it fails. Object.create(Object.create({cool: "joes"})) isn't an object literal so it rightly returns false (tested in modern JS)Undercoat
V
8

It sounds like you are looking for this:

function Foo() {}

var a = {};
var b = new Foo();

console.log(a.constructor == Object); // true
console.log(b.constructor == Object); // false

The constructor property on an object is a pointer to the function that is used to construct it. In the example above b.constructor == Foo. If the object was created using curly brackets (the array literal notation) or using new Object() then its constructor property will == Object.

Update: crescentfresh pointed out that $(document).constructor == Object rather than being equal to the jQuery constructor, so I did a little more digging. It seems that by using an object literal as the prototype of an object you render the constructor property almost worthless:

function Foo() {}
var obj = new Foo();
obj.constructor == Object; // false

but:

function Foo() {}
Foo.prototype = { objectLiteral: true };
var obj = new Foo();
obj.constructor == Object; // true

There is a very good explanation of this in another answer here, and a more involved explanation here.

I think the other answers are correct and there is not really a way to detect this.

Vernverna answered 23/7, 2009 at 22:10 Comment(2)
constructor === Object is true for lots of objects. For example try javascript:alert($(document).constructor === Object) on this page, even though jQuery !== Object.Howbeit
This method will throw a error if the test "object" is nullCalisaya
S
4

An object literal is the notation you use to define an object - which in javascript is always in the form of a name-value pair surrounded by the curly brackets. Once this has been executed there is no way to tell if the object was created by this notation or not (actually, I think that might be an over-simplification, but basically correct). You just have an object. This is one of the great things about js in that there are a lot of short cuts to do things that might be a lot longer to write. In short, the literal notation replaces having to write:

var myobject = new Object();
Sonny answered 23/7, 2009 at 18:37 Comment(0)
B
4

I had the same issue, so I decide to go this way:

function isPlainObject(val) {
  return val ? val.constructor === {}.constructor : false;
}
// Examples:
isPlainObject({}); // true
isPlainObject([]); // false
isPlainObject(new Human("Erik", 25)); // false
isPlainObject(new Date); // false
isPlainObject(new RegExp); // false
//and so on...
Bali answered 12/10, 2014 at 11:26 Comment(3)
@Quentin Engles, why not ? I've tested it in different browsers, try to run it yourself.Bali
Like when the constructor property is set to Object on an object with a constructor. Then again if all a person wanted was to tell if something were a literal, and not if it weren't then I don't know.Rab
fails with isPlainObject( JSON ) (returns true)Consume
D
2

There is no way to tell the difference between an object built from an object literal, and one built from other means.

It's a bit like asking if you can determine whether a numeric variable was constructed by assigning the value '2' or '3-1';

If you need to do this, you'd have to put some specific signature into your object literal to detect later.

Disused answered 23/7, 2009 at 18:28 Comment(0)
M
2

Nowaday there is a more elegant solution that respond exactly to your question:

function isObject(value) {
  return value !== null && value !== undefined && Object.is(value.constructor, Object)
}

// Test stuff below //

class MyClass extends Object {
  constructor(args) {
    super(args)
  }
  say() {
    console.log('hello')
  }
}

function MyProto() {
  Object.call(this)
}
MyProto.prototype = Object.assign(Object.create(Object.prototype), {

  constructor: MyProto,

  say: function() {
    console.log('hello')
  }

});

const testsCases = {
  objectLiteral: {},
  objectFromNew: new Object(),
  null: null,
  undefined: undefined,
  number: 123,
  function: new Function(),
  array: new Array([1, 2, 3]),
  string: new String('foobar'),
  image: new Image(),
  bool: true,
  error: new Error('oups'),
  myClass: new MyClass(),
  myProto: new MyProto()
}

for (const [key, value] of Object.entries(testsCases)) {
  console.log(`${key.padEnd(15)} => ${isObject(value)}`)
}

Best regards

Monarchism answered 17/7, 2020 at 19:54 Comment(0)
O
1
typeof obj === 'object' && obj !== null && Object.getPrototypeOf(obj) === Object.prototype

below all return false

123
null
undefined
'abc'
false
true
[]
new Number()
new Boolean()
() => {}
function () {}

an improvement over jesse's answer

Oersted answered 28/2, 2022 at 10:28 Comment(1)
Definitely better than Jesse's answer but it still doesn't address the fact that with the ability to change prototypes there is no fool proof way to tell if an object was created with an object literal or not. However there is one additional check you can do to help narrow it down since Object.prototype.toString.call({})=='[object Object]' is typically always true.Undercoat
C
0

This is what works for me

const isObjectLiteral = (val) => {
    return (
        val &&
        Object.prototype.toString.call(val) === '[object Object]' &&
        ((val as ObjectLiteral).constructor === Object ||
            Object.getPrototypeOf(val) === null)
    )
}

All of this will check, nothing else:

  • {...}
  • new Object({})
  • Object.create(null)
  • Object.create({sample: 12})
Caves answered 2/4 at 2:48 Comment(0)
D
-1

11 year old question here is my tidy solution, open to edge case suggestions; steps -> look for objects only then compare to check properties -> object literals do not have length, prototype and for edge case stringyfy properties.

tried in test for JSON and Object.create(Object.create({cool: "joes"})).

 "use strict"
let isObjectL = a => { 
        if (typeof a !=='object' || ['Number','String','Boolean', 'Symbol'].includes(a.constructor.name)) return false;
       let props = Object.getOwnPropertyNames(a);
        if ( !props.includes('length') && !props.includes('prototype') || !props.includes('stringify')) return true;
         };


let A={type:"Fiat", model:"500", color:"white"};
let B= new Object();
let C = { "name":"John", "age":30, "city":"New York"};
let D= '{ "name":"John", "age":30, "city":"New York"}';
let E = JSON.parse(D);
let F = new Boolean();
let G = new Number();
    
    console.log(isObjectL(A));
    
    console.log(isObjectL(B));
    
    console.log(isObjectL(C));
    
    console.log(isObjectL(D));
    
    console.log(isObjectL(E));
    
    console.log(isObjectL(JSON));
    
    console.log(isObjectL(F));
    
    console.log(isObjectL(G));
    
    console.log(isObjectL(
Object.create(Object.create({cool: "joes"}))));

    
    console.log(isObjectL());

Another variant showing inner working

isObject=function(a) { 
    let exclude = ['Number','String','Boolean', 'Symbol'];
    let types = typeof a;
    let props = Object.getOwnPropertyNames(a);
    console.log((types ==='object' && !exclude.includes(a.constructor.name) &&
    ( !props.includes('length') && !props.includes('prototype') && !props.includes('stringify'))));
    return `type: ${types} props: ${props}
    ----------------`}
    
    A={type:"Fiat", model:"500", color:"white"};
B= new Object();
C = { "name":"John", "age":30, "city":"New York"};
D= '{ "name":"John", "age":30, "city":"New York"}';
E = JSON.parse(D);
F = new Boolean();
G = new Number();
    
    console.log(isObject(A));
    
    console.log(isObject(B));
    
    console.log(isObject(C));
    
    console.log(isObject(D));
    
    console.log(isObject(E));
    
    console.log(isObject(JSON));
    
    console.log(isObject(F));
    
    console.log(isObject(G));
    
    console.log(isObject(
Object.create(Object.create({cool: "joes"}))));
Dewie answered 10/2, 2021 at 13:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.