2009-07-23 24 views
13

¿Hay alguna manera de determinar en Javascript si un objeto se creó usando la notación object-literal o usando un método de constructor?¿Cómo determinar si un objeto es un objeto literal en Javascript?

Me parece que acaba de acceder a su objeto principal, pero si el objeto que está pasando no tiene una referencia a su padre, no creo que pueda decir esto, ¿o sí?

+0

jeresig me pidió que implementara una función que haría exactamente eso por él. – leeand00

+0

Un objeto nunca es un objeto literal, ¿así que puede reformular la pregunta? – Nosredna

+0

Y, por cierto, ¿cuál era tu revista favorita de Commodore 64? – Nosredna

Respuesta

10

me encontré con esta pregunta e hilo durante una hackfest dulce, que implicó una búsqueda del Grial para evaluar si un objeto fue creado con {} o Objeto nuevo() (todavía no me he dado cuenta.)

De todos modos, me sorprendió encontrar la similitud entre la función isObjectLiteral() publicada aquí y mi propia función isObjLiteral() que escribí para el proyecto Pollen.JS . Creo que esta solución fue publicada antes de mi confirmación de Pollen.JS, así que, ¡felicidades! Lo bueno para mí es la longitud ... menos de la mitad (cuando se incluye tu rutina de configuración), pero ambos producen los mismos resultados.

Echale un vistazo:

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

Además, algunas cosas prueba:

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

O en jsbin.com ...

http://jsbin.com/iwuwa

Asegúrese de abrir Firebug cuando llegues allí, la depuración del documento es para amantes de IE.

+0

@Rick, esta solución parece más elegante. – leeand00

+0

@ leeand00: Tiene aproximadamente la misma longitud que la mía. Acabo de incluir una implementación de Object.getPrototypeOf en la mía, que esta ___ no incluye__. –

+0

Además, si elimina la parte 'Object.getPrototypeOf.isNative', la mía es solo 6 SLOC. –

3

No hay forma de ver la diferencia entre un objeto creado a partir de un objeto literal y uno creado a partir de otros medios.

Es como preguntar si se puede determinar si se construyó una variable numérica asignando el valor '2' o '3-1';

Si necesita hacer esto, tendrá que poner una firma específica en su objeto literal para detectarlo más tarde.

3

Un objeto literal es la notación que utiliza para definir un objeto, que en javascript siempre tiene la forma de un par nombre-valor rodeado por las llaves. Una vez que se ha ejecutado esto, no hay forma de saber si el objeto fue creado por esta notación o no (en realidad, creo que podría ser una simplificación excesiva, pero básicamente correcta). Solo tienes un objeto. Esta es una de las mejores cosas de js, ya que hay muchos atajos para hacer cosas que podrían ser mucho más largas de escribir. En resumen, la notación literal reemplaza tener que escribir:

var myobject = new Object(); 
3

Suena como que busca esto:

function Foo() {} 

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

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

La propiedad constructor sobre un objeto es un puntero a la función que se utiliza para construirlo. En el ejemplo anterior b.constructor == Foo. Si el objeto se creó usando llaves (la notación literal de la matriz) o usando new Object(), entonces su propiedad de constructor será == Object.

Actualización: crescentfresh señaló que $(document).constructor == Object en lugar de ser igual al constructor jQuery, así que hice un poco más de excavación. Parece que mediante el uso de un objeto literal como el prototipo de un objeto renderizar la propiedad constructor casi sin valor:

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

pero:

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

Hay una muy buena explicación de esto en otra respuesta here y una explicación más complicada here.

Creo que las otras respuestas son correctas y no hay realmente una manera de detectar esto.

+1

'constructor === Object' es cierto para muchos objetos. Por ejemplo, intente 'javascript: alert ($ (document) .constructor === Object)' en esta página, aunque 'jQuery! == Object'. –

+0

He actualizado mi respuesta en función del comentario de crescentfresh. – Prestaul

11

Editar: estoy interpretando "objeto literal" como cualquier cosa creada usando un objeto literal o la Object constructor. Esto es lo que probablemente John Resig quiso decir.

Tengo una función que funcionará incluso si .constructor ha sido contaminado o si el objeto fue creado en otro marco. Tenga en cuenta que Object.prototype.toString.call(obj) === "[object Object]" (como algunos creen) no resolverá este problema.

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; 

Aquí está el código HTML para el caso de prueba:

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

Excelente respuesta, y es bueno tenerte en stackoverflow. Fuera del tema, pero también gracias a los métodos de matriz e4x. – Prestaul

+0

Ahora que he leído su respuesta, creo ** Entiendo la pregunta. – tvanfosson

+1

¡Whoa! ¿Cuál es el signo igual de 3x (===)? – leeand00

2

que tenían el mismo problema, por lo que decide seguir este camino:

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

¿Es esto confiable? –

+0

@Quentin Engles, ¿por qué no? Lo he probado en diferentes navegadores, intente ejecutarlo usted mismo. – a8m

+0

Al igual que cuando la propiedad del constructor se establece en Objeto en un objeto con un constructor. Entonces otra vez si todo lo que una persona deseaba era decir si algo era literal, y si no lo era, entonces no lo sé. –

0

Lo que queremos es:

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

Esto comprueba que el objeto es un objeto sin formato creado con cualquiera new Object() o {...} y no alguna subclase de Object.

Cuestiones relacionadas