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

Desarrollando Skills Alexa con AWS Lambda y node.js por Germán Viscuso

Germán Viscuso, evangelista de la tecnología Alexa para España, nos cuenta en esta charla las posibilidades nos ofrece la tecnología de Amazon para crear Skills personalizados desarrollados en NodeJS y desplegados en Lamdas de AWS. Un nuevo mundo de posibilidades para los desarrolladores.

Native apps with Titanium por Rene Pot

Rene Pot nos cuenta cómo crear apps nativas con Titanium + Alloy y sacar el máximo partido en el desarrollo de aplicaciones nativas desde un único código fuente basado en Javascript.

10 patrones de diseño para Node por Felipe Polo

Los diez patrones de diseño para Javascript presentados por Felipe Polo en esta interesante charla te ayudarán a crear un código más legible, mantenible y comunicativo. Son un buen punto de partida para hacer mejor tus programas.

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.

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

Cómo diferenciar arrow function de function

En un reciente artículo Javier Vélez Reyes hace patente las principales diferencias entre las funciones tradicionales y las funciones flecha, ya que ambos modelos no son equivalentes e intercambiables. Veamos cómo es posible saber si una función ha sido construida por medio de la instrucción function o como una arrow function.