如何在Javascript中确定一个对象是否为对象字面量?

27

有没有办法在Javascript中确定对象是使用对象字面量符号创建的还是使用构造函数方法创建的?

我认为你只需访问它的父对象,但如果您传入的对象没有对其父对象的引用,那么您似乎无法判断这一点,不是吗?


Jeremy Resig让我为他实现一个能够做到这一点的函数。 - leeand00
对象从来不是对象文字,所以你能否重新表达问题? - Nosredna
顺便问一下,你最喜欢的Commodore 64杂志是哪个? - Nosredna
@Nosredna 我想我当时太小了,我三岁时就拥有了我的第一台C64。我所知道的只是整个:load "*", 8, 1 这样的东西,所以我当时只能加载我的游戏。 - leeand00
10个回答

23

你想要的是:

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

这个检查确保对象是使用new Object(){...}创建的普通对象,而不是Object的子类。


13
我在一次有趣的黑客马拉松中偶然发现了这个问题和线程,当时我们正在寻找一种方法来评估一个对象是使用{}还是new Object()创建的(我仍然没有弄清楚)。不管怎样,我很惊讶地发现此处发布的 isObjectLiteral() 函数与我为 Pollen.JS 项目编写的 isObjLiteral() 函数相似。我认为这个解决方案是在我提交 Pollen.JS 之前发布的,所以向你致敬!我的函数优点是长度较短,只有一半(包括设置程序),但两者产生的结果相同。
看一下:
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;
                })()
              )
          );
}
另外,一些测试内容:
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(); }
或者在 jsbin.com 上... http://jsbin.com/iwuwa 请确保打开 Firebug,当你到达那里时 - 调试到文档是为 IE 爱好者准备的。

由于Object.getPrototypeOf在IE 8中不存在,因此这无法工作。因此长度更短。对于某些getPrototypeOf的实现(例如John Resig的实现),它会永远运行下去,因为它可以在特殊情况下返回“undefined”。 - Kato
2
嗯,你能解释一下为什么在 while 循环条件中你写了 !false 而不是 true 吗? - th3n3rd
当传入undefinednull时,我发现程序会出现错误。 - jmealy
2
为什么不直接使用 Object.getPrototypeOf(Object.getPrototypeOf(_obj))===null 呢? - Manish Kumar
失败原因为 isObjLiteral(JSON) // 返回 true - colxi
显示剩余4条评论

11

编辑:我将“对象字面量”解释为使用对象字面量创建的任何内容使用Object构造函数创建的任何内容。这很可能是约翰·雷西格(John Resig)的意思。

我有一个函数,即使.constructor被污染或者对象是在另一个框架中创建的,它也能正常工作。请注意,Object.prototype.toString.call(obj) === "[object Object]"(一些人可能会这样认为)将无法解决此问题。

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;

以下是测试用例的HTML代码:

<!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>

1
很棒的回答,很高兴在stackoverflow上有你。虽然题外话,但也感谢你提供的e4x数组方法。 - Prestaul
既然我已经阅读了您的答案,我“想”我理解了这个问题。 - tvanfosson
1
哇!三个等号(===)是什么意思? - leeand00
1
@leeand00它的意思是“完全等于”。例如: var x = {value​​Of:function(){return 1}}; (x == 1) === true; (x === 1) === true; - Eli Grey
第二个例子应该以 === false 结束,我的意思是。 - Eli Grey
在原型中具有原型的对象失败:isObjectLiteral(Object.create(Object.create({cool: "joes"})))。 - surgemcgee

8

听起来你正在寻找这个:

function Foo() {}

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

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

在对象上的构造函数属性是指向用于构造它的函数的指针。在上面的例子中,b.constructor == Foo。如果使用花括号(数组文字表示法)或使用new Object()创建对象,则其构造函数属性将== Object
更新:crescentfresh指出$(document).constructor == Object而不是等于jQuery构造函数,因此我进行了更多的挖掘。似乎通过将对象文字用作对象的原型,您几乎可以使构造函数属性毫无价值。
function Foo() {}
var obj = new Foo();
obj.constructor == Object; // false

但是:

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

这个问题在这里有一个非常好的解释,这里有更详细的解释。

我认为其他答案是正确的,实际上没有一种方法可以检测到这个问题。


1
“constructor === Object” 对于许多对象都是成立的。例如,在此页面上尝试 javascript:alert($(document).constructor === Object),即使 jQuery !== Object 也是如此。 - Crescent Fresh
如果测试“对象”为null,此方法将抛出一个错误。 - Luke

4

我遇到了同样的问题,所以我决定这样做:

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...

@Quentin Engles,为什么不呢? 我已经在不同的浏览器中测试过了,请您自己运行一下。 - a8m
当将构造函数属性设置为Object时,对象上具有构造函数。但是,如果一个人只想判断某个东西是否是字面量,而不是它不是,则我不知道。 - Quentin Engles
失败原因为 isPlainObject( JSON ) 返回 true。 - colxi

4
对象字面量是定义对象的符号表示法——在javascript中,它始终采用由花括号包围的名称-值对形式。一旦执行了这个操作,就无法判断对象是否是通过此符号表示法创建的(实际上,我认为这可能是一种过度简化,但基本上是正确的)。你只是有一个对象。这是js的伟大之处之一,因为有很多捷径可以做一些可能需要编写更长的代码的事情。简而言之,字面表示法替代了以下代码的编写:
var myobject = new Object();

2

无法区分从对象文字创建的对象和其他方法创建的对象之间的区别。

这有点像询问您是否可以确定数值变量是通过赋值“2”或“3-1”构建的。

如果您需要这样做,您必须在对象文字中放入一些特定的签名以供以后检测。


2
现在有一种更优雅的解决方案,可以完全回答您的问题:

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)}`)
}

致以最好的问候


1
typeof obj === 'object' && obj !== null && Object.getPrototypeOf(obj) === Object.prototype

以下所有返回值均为false。
123
null
undefined
'abc'
false
true
[]
new Number()
new Boolean()
() => {}
function () {}

一个比Jesse的答案更好的改进

0

这是一个11年前的问题,这是我的整洁解决方案,欢迎提出边缘情况的建议; 步骤 -> 只查找对象,然后比较以检查属性 -> 对象文字没有长度、原型和边缘情况下的字符串属性。

在JSON和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());

另一种显示内部工作的变体

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"}))));


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接