Seleccionar página

Las propiedades de los objetos en Javascript son propiedades enumerables y no enumerables, es decir, son enumerables si son obtenidas por for in o Object.keys(objeto), son no enumerables si no son obtenidas por estas instrucciones y funciones, aunque estén presentes en el objeto.

Por ejemplo, las propiedades de los objetos nativos de Javascript están definidas como no enumerables, por lo que cuando utilizamos bucles for in sobre estos objetos no se obtiene estas propiedades, aunque estén presentes dentro de la cadena de prototipos y sean propiedades heredadas (ver Propiedades propias y heredadas de los objetos).

Por ello, para comprobar si una propiedad está presente en un objeto no podremos utilizar simplemente for in, ni tampoco vamos a poder utilizar Object.keys(objecto), ya que si la propiedad es no enumerable estará presente, aunque no la obtengamos de esta forma.

Sí podemos utilizar el operador in. No se suele ver mucho el uso de este operador y por el contrario se encuentran formas mucho más complejas o, incluso erróneas, de comprobar la existencia de una propiedad en un objeto, por ejemplo, comprobamos si la propiedad es verdadera, ya que puede esta contener false o null.

var objeto = {
    ok: false
};

// Forma correcta de comprobar la existencia de una propiedad
if ('ok' in objeto) {
    console.log("'ok' in objeto");          // Se muestra
}
if ('toString' in objeto) {
    console.log("'toString' in objeto");    // Se muestra
}

// Forma inconsistente de comprobar la existencia de una propiedad
if (objeto.ok) {
    console.log("objeto.ok");               // No se muestra y debería
}
if (objeto.toString) {
    console.log("objeto.toString");         // Se muestra
}

// Forma correcta y habitual de comprobar la existencia de una propiedad
if (typeof objeto.ok !== 'undefined') {
    console.log("typeof objeto.ok !== 'undefined'");        // Se muestra
}
if (typeof objeto.toString !== 'undefined') {
    console.log("typeof objeto.toString !== 'undefined'");  // Se muestra
}

// Forma compleja de comprobar la existencia de una propiedad
// que sólo funciona para propiedades propias
if (Object.keys(objeto).indexOf('ok') !== -1) {
    console.log("Object.keys(objeto).indexOf('ok') !== -1");        // Se muestra
}
if (Object.keys(objeto).indexOf('toString') !== -1) {
    console.log("Object.keys(objeto).indexOf('toString') !== -1");  // No se muestra
}

Por otra parte, para saber si una propiedad es enumerable o no podemos utilizar dos útiles métodos. El primero es objeto.propertyIsEnumerable("propiedad"). Este método devuelve true si la propiedad es enumerable y false si no lo es.

var objeto = {
    ok: false
};

if (objeto.propertyIsEnumerable('ok')) {
    console.log("'ok' es enumerable");          // Se muestra
} else {
    console.log("'ok' no es enumerable");
}

if (objeto.propertyIsEnumerable('toString')) {
    console.log("'toString' es enumerable");
} else {
    console.log("'toString' no es enumerable"); // Se muestra
}

Otra forma, más completa, de comprobar las características de las propiedades es utilizar Object.getOwnPropertyDescriptor(objeto). Este método sólo funciona con propiedades propias y no funciona con propiedades heredadas de la cadena de prototipos. Devuelve un objeto con información completa sobre la propiedad.

var objeto = {
    ok: false
};
console.log(Object.getOwnPropertyDescriptor(objeto, 'ok'));
// Mostrará algo parecido a esto
// { value: false,
//   writable: true,
//   enumerable: true,
//   configurable: true }

console.log(Object.getOwnPropertyDescriptor(Object.prototype, 'toString'));
// Mostrará algo parecido a esto
// { value: [Function: toString],
//   writable: true,
//   enumerable: false,
//   configurable: true }

Nosotros podemos definir propiedades no enumerables sobre cualquier objeto a través de descriptores donde se definen las propiedades y sus características. Para ellos podemos utilizar:

  • Object.create(prototipo, descriptores)
  • Object.defineProperty(objeto, "propiedad", descriptor)
  • Object.defineProperties(objeto, descriptores)

Básicamente lo que se utiliza en estos métodos es un descriptor, es decir, una estructura similar que la que devuelve Object.getOwnPropertyDescriptor(objeto,"propiedad") donde se describen las características de las propiedades.

// Se crea un objeto con un descriptor de sus propiedadades
var objeto = Object.create(Object, {
    ok: {
        value: false,
        enumerable: false
    },
    num: {
        value: 10,
        enumerable: true
    }
});

// Se añade una propiedad no enumerable a todos los objetos
Object.defineProperty(objeto.prototype, 'debug',
    {   value: function() {
                //...
            },
        enumerable: false
    }
);

// Se comprueban las propiedades enumerables (propias y heredadas)
for (var prop in objeto) {
    console.log(prop);  // num
}

// Se comprueban las propiedades enumerables (propias)
Object.keys(objeto).forEach(function(prop) {
    console.log(prop);  // num
});

// Se comprueba que existe la propiedad 'debug'
if ('debug' in objeto) {
    console.log("'debug' in objeto");   // Se muestra
}

Como hemos venido explicando, Object.keys(objeto) y for in sólo muestran propiedades enumerables. En ocasiones vamos a necesitar conocer los nombres de todas las propiedades, indiferentemente de si son enumerables o no, para ello se debe utilizar Object.getOwnPropertyNames(objeto). Con este método vamos a obtener todos las propiedades propias (no las heredadas) de un objeto.

// Se crea un objeto con un descriptor de sus propiedadades
var objeto = Object.create(Object, {
    ok: {
        value: false,
        enumerable: false
    },
    num: {
        value: 10,
        enumerable: true
    }
});

// Se añade una propiedad no enumerable a todos los objetos
Object.defineProperty(objeto.prototype, 'debug',
    {   value: function() {
                //...
            },
        enumerable: false
    }
);

// Se comprueban las propiedades enumerables (propias)
Object.getOwnPropertyNames(objeto).forEach(function(prop) {
    console.log(prop);  // ok, num
});

Quizás pueda parecer que estamos complicando mucho la creación de objetos y que en la mayoría de las ocasiones no será necesario trabajar a este nivel de configuración de las propiedades de un objeto, pero lo cierto es que definir una propiedad como no enumerable puede tener bastantes ventajas.

Si reescribimos una propiedad como toString() está pasa a ser enumerable si no la definimos por medio de Object.defineProperty(). También es útil para añadir propiedades a un objeto que ya estamos utilizando y no afectar al comportamiento previsto de los bucles for in o las llamadas a Object.keys(objeto) que no esperan recibir estas nuevas propiedades.

Javascript ofrece mucha más flexibilidad a la hora de trabajar con objetos y propiedades de la que se suele utilizar. Conocer y aprovechar estas características nos permite desarrollar programas más robustos y capaces de adaptarse a contextos de uso diferentes sin que se vea afectado su funcionamiento.

Novedades

Datos inmutables en Javascript

Datos inmutables en Javascript

En Javascript todo parece mutable, es decir, que se puede cambiar, pero lo cierto es que también nos ofrece varios mecanismos para conseguir que los datos que manejamos, especialmente los objetos, sean inmutables. Te invitamos a descubrir cómo…

Copiar objetos en Javascript

Copiar objetos en Javascript

Copiar objetos no es algo sencillo, incluso se podría decir que en si mismo no es posible, ya que el concepto «copiar» no entra dentro del paradigma de los objetos. No obstante, por medio de instrucciones como Object.assign() hemos aprendido como obtener objetos con las mismas propiedades, pero está técnica no se puede aplicar a todos los tipos de objetos disponibles en Javascript. Vamos a ver cómo podemos copiar cualquier tipo de objeto…

Descubre los Javascript Array Internals

Descubre los Javascript Array Internals

El Array es una de las estructuras más utilizadas en Javascript y no siempre bien comprendida. Hoy os invitamos a analizar el comportamiento interno de este objeto y descubrir cómo Javascript implementa las diferente acciones con los Array y que operaciones internas se realizan en cada caso.

Web Components: pasado, presente y futuro

Web Components: pasado, presente y futuro

Los Web Components aparecieron en el panorama de desarrollo hace ya bastante tiempo. Desde su presentación se les ha prestado mucha atención, pero lo cierto es que no han sido acogidos de forma generalizada, quizás por la difusión de nuevos y potentes frameworks. Nos preguntamos qué ha pasado con este estándar y, sobre todo, que puede pasar de aquí en adelante con el uso práctico de los componentes web.

breves

Descrubir algunas características de console

En el día a día nos encontramos muy a menudo utilizando console. Es una navaja multiusos que nos facilita la vida a la hora de depurar nuestro código. La mayoría de nosotros ha utilizado console.log(), pero tiene otras muchas funcionalidades.

Matrices dispersas o sparse arrays en JS

Una característica que puede producir algunos problemas, si no lo tenemos en cuenta, es la posibilidad de tener matrices con huecos, es decir, con algunos de sus elementos sin definir. Es lo que se suele denominar una matriz dispersa o sparse array. Veamos cómo trabajar con esta características de las matrices.

Operadores de bits usados con asiduidad

Cada día más a menudo podemos encontrar operadores binarios utilizados como formas abreviadas de algunas operaciones que de otra forma sería algo menos compactas y, quizás, más comprensibles. Veamos algunos casos en detalle.

Obtener todas las propiedades de un objeto

¿Cómo podemos obtener absolutamente todas las propiedades de un objeto? No disponemos de un método nativo para este propósito, pero en unas pocas lineas podemos construir una función para nuestro propósito.