¿Cómo puedo indique la diferencia entre un objeto literal y cualquier otro objeto Javascript (por ejemplo, un nodo DOM, un objeto Date, etc.)?
La respuesta corta es que no puede.
Un objeto literal es algo así como:
var objLiteral = {foo: 'foo', bar: 'bar'};
mientras que el mismo objeto creado usando el constructor de objetos podría ser:
var obj = new Object();
obj.foo = 'foo';
obj.bar = 'bar';
no creo que hay alguna fiable forma de ver la diferencia entre cómo se crearon los dos objetos.
¿Por qué es importante?
Una estrategia de prueba de características generales es probar las propiedades de los objetos pasados a una función para determinar si son compatibles con los métodos que deben invocarse. De esta forma, realmente no te importa cómo se crea un objeto.
Puede emplear el "tipado de pato", pero solo de forma limitada. No puede garantizarlo solo porque un objeto tenga, por ejemplo, un método getFullYear()
que sea un objeto Date. Del mismo modo, solo porque tiene una propiedad nodeType no significa que sea un objeto DOM.
Por ejemplo, la función jQuery isPlainObject
piensa que si un objeto tiene una propiedad nodeType, es un nodo DOM, y si tiene una propiedad setInterval
es un objeto Window. Ese tipo de mecanografía de pato es extremadamente simplista y fallará en algunos casos.
También puede observar que jQuery depende de que las propiedades se devuelvan en un orden específico - otra suposición peligrosa que no es compatible con ningún estándar (aunque algunos partidarios están tratando de cambiar el estándar para adaptarse a su supuesto comportamiento).
Editar 22-Apr-2014: En la versión 1.10 jQuery incluye un support.ownLast propiedad basado en las pruebas de una sola propiedad (al parecer esto es para apoyo IE9) para ver si las propiedades heredadas se enumeran primera o la última. Esto continúa ignorando el hecho de que las propiedades de un objeto pueden devolverse en cualquier orden, independientemente de si son heredadas o propias, y pueden estar mezcladas.
Probablemente, la prueba más simple para los objetos "natural" es:
function isPlainObj(o) {
return typeof o == 'object' && o.constructor == Object;
}
que siempre será cierto para los objetos creados con objetos literales o el constructor de objetos, pero también puede dar resultados falsos para objetos creados otras formas y puede (probablemente) fallar en todos los cuadros. También podría agregar una prueba instanceof
, pero no puedo ver que haga algo que la prueba de constructor no haga.
Si está pasando objetos ActiveX, lo mejor es envolverlo en try..catch ya que pueden devolver todo tipo de resultados extraños, incluso arrojar errores.
Edición 13-oct-2015
Por supuesto, hay algunas trampas:
isPlainObject({constructor: 'foo'}); // false, should be true
// In global scope
var constructor = Object;
isPlainObject(this); // true, should be false
ensuciar con la propiedad constructor causará problemas. También existen otras trampas, como objetos creados por constructores distintos de Object.
Como ES5 ahora es bastante omnipresente, hay Object.getPrototypeOf para verificar el [[Prototype]]
de un objeto. Si es el objeto Object.prototype, entonces el objeto es un objeto simple. Sin embargo, algunos desarrolladores desean crear objetos verdaderamente "vacíos" que no tengan propiedades heredadas. Esto se puede hacer usando:
var emptyObj = Object.create(null);
En este caso, la propiedad es [[Prototype]]
nula. Así que simplemente verificando si el prototipo interno es Object.prototype no es suficiente.
También existe la ampliamente utilizada razonablemente:
Object.prototype.toString.call(valueToTest)
que se especificó como devolver una cadena basada en la [[Class]]
propiedad interna, que es para los objetos [object Object]. Sin embargo, eso ha cambiado en ECMAScript 2015, por lo que las pruebas se realizan para otros tipos de objetos y el valor predeterminado es [object Object], por lo que el objeto puede no ser un "objeto simple", solo uno que no se reconoce como algo más. Por lo tanto, la especificación señala que:
"[toString pruebas utilizando] no proporciona un mecanismo de tipo de pruebas fiables para otros tipos de objetos incorporados o programa definidos"
http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.prototype.tostring
lo tanto una función de actualización que permite anfitriones pre-ES5, objetos con un [[Prototype]]
de tipos de objetos nulos y otros que no tienen getPrototypeOf (como nula, gracias Chris Nielsen) Esta abajo.
Nota que no hay manera de polyfill getPrototypeOf, por lo que puede no ser útil si se requiere apoyo para navegadores anteriores (por ejemplo IE 8 e inferior, de acuerdo con MDN).
/* Function to test if an object is a plain object, i.e. is constructed
** by the built-in Object constructor and inherits directly from Object.prototype
** or null. Some built-in objects pass the test, e.g. Math which is a plain object
** and some host or exotic objects may pass also.
**
** @param {} obj - value to test
** @returns {Boolean} true if passes tests, false otherwise
*/
function isPlainObject(obj) {
// Basic check for Type object that's not null
if (typeof obj == 'object' && obj !== null) {
// If Object.getPrototypeOf supported, use it
if (typeof Object.getPrototypeOf == 'function') {
var proto = Object.getPrototypeOf(obj);
return proto === Object.prototype || proto === null;
}
// Otherwise, use internal class
// This should be reliable as if getPrototypeOf not supported, is pre-ES5
return Object.prototype.toString.call(obj) == '[object Object]';
}
// Not an object
return false;
}
// Tests
var data = {
'Host object': document.createElement('div'),
'null' : null,
'new Object' : {},
'Object.create(null)' : Object.create(null),
'Instance of other object' : (function() {function Foo(){};return new Foo()}()),
'Number primitive ' : 5,
'String primitive ' : 'P',
'Number Object' : new Number(6),
'Built-in Math' : Math
};
Object.keys(data).forEach(function(item) {
document.write(item + ': ' + isPlainObject(data[item]) + '<br>');
});
http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object – Ben
@Ben, ahora que tengo actualizó la pregunta, no creo que sea un duplicado de esa. Pero con mucho gusto lo cerraré si es un duplicado de una pregunta diferente. –
Esto es casi un duplicado, pero ¿hay alguna solución que no cause falsos positivos cuando se le da un objeto definido por el usuario? http://stackoverflow.com/questions/4320767/check-that-value-is-object-literal –