recorrer objetos objeto new literales eliminar elemento crear buscar array agregar javascript types comparison object-literal

new - recorrer array de objetos javascript



¿Cómo puedo diferenciar entre un objeto literal y otros objetos Javascript? (5)

Actualización : estoy reformulando esta pregunta, porque el punto importante para mí es identificar el objeto literal:

¿Cómo puedo saber la diferencia entre un objeto literal y cualquier otro objeto Javascript (por ejemplo, un nodo DOM, un objeto Date, etc.)? ¿Cómo puedo escribir esta función:

function f(x) { if (typeof x === ''object literal'') console.log(''Object literal!''); else console.log(''Something else!''); }

¡Para que solo se imprima Object literal! Como resultado de la primera llamada a continuación:

f({name: ''Tom''}); f(function() {}); f(new String(''howdy'')); f(''hello''); f(document);

Pregunta original

Estoy escribiendo una función de Javascript que está diseñada para aceptar un objeto literal, una cadena o un nodo DOM como su argumento. Debe manejar cada argumento de manera ligeramente diferente, pero en este momento no puedo encontrar la manera de diferenciar entre un nodo DOM y un objeto literal simple.

Aquí hay una versión enormemente simplificada de mi función, junto con una prueba para cada tipo de argumento que necesito manejar:

function f(x) { if (typeof x == ''string'') console.log(''Got a string!''); else if (typeof x == ''object'') console.log(''Got an object literal!''); else console.log(''Got a DOM node!''); } f(''hello''); f({name: ''Tom''}); f(document);

Este código registrará el mismo mensaje para las segundas dos llamadas. No puedo averiguar qué incluir en la cláusula else if . He intentado otras variaciones como x instanceof Object que tienen el mismo efecto.

Entiendo que esto podría ser un mal diseño de API / código por mi parte. Incluso si lo es, me gustaría saber cómo hacerlo.


¿Cómo puedo saber 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 puedes.

Un objeto literal es algo 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 haya una manera confiable de diferenciar cómo se crearon los dos objetos.

¿Por qué es importante?

Una estrategia general de prueba de características es probar las propiedades de los objetos pasados ​​a una función para determinar si son compatibles con los métodos a llamar. De esa manera no te importa realmente cómo se crea un objeto.

Puede emplear el "tipeo de pato", pero solo hasta cierto punto. No puede garantizar que solo porque un objeto tenga, por ejemplo, un método getFullYear() que sea un objeto Date. Del mismo modo, solo porque tenga 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 escritura de pato es extremadamente simplista y fallará en algunos casos.

También puede tener en cuenta 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 intentando cambiar el estándar para adaptarse a su comportamiento asumido).

Editar 22 de abril de 2014: en la versión 1.10 jQuery incluye una propiedad support.ownLast basada en pruebas de una sola propiedad (aparentemente esto es para el soporte de IE9) para ver si las propiedades heredadas se enumeran primero o al final. 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 ser mezcladas.

Probablemente la prueba más simple para objetos "simples" es:

function isPlainObj(o) { return typeof o == ''object'' && o.constructor == Object; }

Lo que siempre será cierto para los objetos creados con literales de objetos o el constructor de objetos, pero bien puede dar resultados falsos para objetos creados de otras maneras y puede (probablemente lo hará) fallar en los marcos. También podría agregar una instanceof prueba, pero no puedo ver que haga algo que la prueba del constructor no haga.

Si está pasando objetos ActiveX, es mejor envolverlo en try..catch, ya que pueden devolver todo tipo de resultados extraños, incluso errores de lanzamiento.

Editar 13-Oct-2015

Por supuesto que hay algunas trampas:

isPlainObject( {constructor: ''foo''} ); // false, should be true // In global scope var constructor = Object; isPlainObject( this ); // true, should be false

Jugar con la propiedad del constructor causará problemas. También hay otras trampas, como los objetos creados por constructores que no sean Objeto.

Dado que ES5 es ahora bastante ubicuo, existe Object.getPrototypeOf para verificar el [[Prototype]] de un objeto. Si es el buit – en Object.prototype , entonces el objeto es un objeto plano. Sin embargo, algunos desarrolladores desean crear objetos realmente "vacíos" que no tienen propiedades heredadas. Esto se puede hacer usando:

var emptyObj = Object.create(null);

En este caso, la propiedad [[Prototype]] es nula . Así que simplemente verificar si el prototipo interno es Object.prototype no es suficiente.

También existe el uso razonablemente amplio:

Object.prototype.toString.call(valueToTest)

que se especificó como devolver una cadena basada en la propiedad interna [[Class]] , que para Objetos es [objeto Objeto]. 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 [objeto Objeto], por lo que el objeto puede no ser un "objeto simple", solo uno que no se reconoce como otra cosa. Por lo tanto, la especificación señala que:

"[testing using toString] no proporciona un mecanismo de prueba de tipo confiable para otros tipos de objetos incorporados o definidos por programa".

http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.prototype.tostring

Por lo tanto, a continuación se muestra una función actualizada que permite hosts previos a ES5, objetos con [[Prototype]] de null y otros tipos de objetos que no tienen getPrototypeOf (como null , gracias a Chris Nielsen ).

Tenga en cuenta que no hay forma de realizar un polyfill getPrototypeOf , por lo que puede no ser útil si se requiere soporte para navegadores más antiguos (por ejemplo, IE 8 y versiones inferiores, según 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>''); });


Como todos los nodos DOM se heredan de la interfaz del nodo, puede intentar lo siguiente:

if(typeof x === ''string'') { //string } else if(x instanceof Node) { //DOM Node } else { //everything else }

Pero no estoy seguro de si esto funciona en versiones anteriores de Internet Explorer


Mueva la verificación para el nodo DOM por encima del objeto literal. Verifique alguna propiedad que exista en un nodo DOM para detectar un nodo. Estoy usando el tipo de nodeType . No es muy infalible, ya que podría pasar un objeto {nodeType: 0 } y eso lo rompería.

if (typeof x == ''string'') { /* string */ } else if (''nodeType'' in x) { /* dom node */ } else if (typeof x == ''object'') { /* regular object */ }

Todas las comprobaciones de tipificación de pato como la de arriba e incluso las comprobaciones de instanceof están destinadas a fallar. Para determinar verdaderamente si el objeto dado es en realidad un nodo DOM, debe usar algo que no sea el objeto pasado.


Similar al ejemplo de @RobG:

function isPlainObject(obj) { return typeof obj === ''object'' // separate from primitives && obj !== null // is obvious && obj.constructor === Object // separate instances (Array, DOM, ...) && Object.prototype.toString.call(obj) === ''[object Object]''; // separate build-in like Math }

PRUEBA:

function isPlainObject(obj) { return typeof obj === ''object'' && obj !== null && obj.constructor === Object && Object.prototype.toString.call(obj) === ''[object Object]''; } var data = { ''{}'': {}, ''DOM element'': document.createElement(''div''), ''null'' : null, ''Object.create(null)'' : Object.create(null), ''Instance of other object'' : new (function 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 + '':<strong>'' + isPlainObject(data[item]) + ''</strong><br>''); });


Tal vez algo como esto?

var isPlainObject = function(value){ if(value && value.toString && value.toString() === ''[object Object]'') return true; return false; };

O este otro enfoque:

var isObject = function(value){ var json; try { json = JSON.stringify(value); } catch(e){ } if(!json || json.charAt(0) !== ''{'' || json.charAt(json.length - 1) !== ''}'') return false; return true; };