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

Clases: miembros estáticos

Los constructores pueden tener miembros estáticos, es decir, métodos y propiedades que residen en el objeto que es la función constructora. Veamos cómo funcionan.

Clases: métodos de acceso y datos privados

Los métodos get/set para controlar el acceso a los datos son uno de los mecanismos que nos ofrece Javascript para mantener nuestros datos fuera de miradas inadecuadas. Esta funcionalidad, junto con WeakMap nos permite implementar una protección bastante razonable de los datos. Veamos cómo…

Clases: métodos

Los métodos son una de las partes más importantes de las clases, en ellos incluimos las funciones que queremos que trabajen sobre nuestro objeto. Los métodos tienen un modelo específico de definición y funcionamiento.

Clases: propiedades

Las propiedades son un elemento básico de los objetos y las clases. Podemos definirlos de varias formas, tanto en el objeto como en el constructor y especificar su comportamiento con precisión. Veamos cómo.

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.