Wextensible

Introducción

Diseñar e implementar alguno de los algoritmos básicos, por ejemplo, una calculadora o un calendario, es un ejercicio recomendable para poner en práctica los conceptos aprendidos sobre programación. Su utilidad final podría ser relativa en tanto que ya existe código de libre disposición sobre estos ejemplos. Pero un calendario es un experimento adecuado para aplicar los conceptos sobre la Programación Orientada a Objetos. Por lo tanto este calendario que presentamos nos servirá como ejercicio final de una serie de temas con nociones básicas sobre la manipulación de objetos en JavaScript.

Si sabe algo sobre objetos supongo que no tendrá necesidad de saber como se construye un calendario, pero en todo caso puede saltarse estos temas e ir directamente al final, al calendario.

Qué es un objeto de JavaScript y para que sirve

Supongamos un coche como un objeto sobre el cual podemos especificar algunas de sus propiedades:

Un comprador en un concesionario de venta podría tener la posibilidad de elegir un coche (en definitiva un objeto) con una configuración de propiedades a su gusto. Incluso después de realizar su elección podría cambiar de opinión y elegir otro color, por ejemplo. El concesionario estaría dispuesto a mostrarle todas las posibilidades. En definitiva, el comprador y el concesionario aplicaran métodos para seleccionar el coche. Es posible que un tiempo después pueda comprar otro más para su hijo (otro objeto), quizás de otra marca, modelo y color.

En este ejemplo trivial se resume lo qué es y qué hace un objeto:

  1. Un objeto es una entidad, algo que existe.
  2. Tiene propiedades que lo definen y diferencian.
  3. Tiene métodos para gestionar sus propiedades y, en general, para manejar el objeto.

Si conseguimos definir en un programa el objeto coche en abstracto, es decir, sin valores previos para sus propiedades pero definiendo claramente los métodos para manejar ese objeto, entonces podríamos crear indefinido número de objetos de la misma clase usando el mismo programa. Esta es, en pocas palabras, la utilidad principal de la Programación Orientada a Objetos (POO). En el ejemplo anterior este "programa" nos serviría para que el padre eligiera su coche y más tarde también lo aplicaríamos para la compra del coche de su hijo.

Un ejemplo de objeto en JavaScript: la clase coche

A continuación se expone el código del objeto coche. Se trata de una function() como el resto de las que se usan en JavaScript. Esta función la podemos denominar como clase del objeto, es decir, es como una plantilla, el programa que definirá el objeto coche en abstracto como decíamos antes. Aunque también se le denomina constructor del objeto, pero en definitiva la clase debe incluir un constructor cuyo nombre coincide con el de la clase, como por ejemplo sucede en Java. Prefiero seguir denominándola clase pues se ajusta más a la idea de plantilla.

Vemos sus propiedades y sus métodos para seleccionar marca, modelo, etc. Hay que decir que las funciones en POO se les suele denominar métodos en lugar de funciones aunque se esté usando function() de JavaScript, pero esto no es más que una cuestión de nomenclatura. Como un script que es, debe situarse en la página para que se cargue antes de usarlo. Puede ponerse en la cabecera de la página dentro de un elemento <script>, aunque siempre se recomienda situar todo el script en archivos externos tal como hemos hecho aquí.

function coche(unaMarca, unModelo, unColor) {
    this.marca = unaMarca
    this.modelo = unModelo
    this.color = unColor

    this.seleccionarMarca = function seleccionarMarca(otraMarca){
        this.marca = otraMarca
    }

    this.seleccionarModelo = function seleccionarModelo(otroModelo){
        this.modelo = otroModelo
    }

    this.seleccionarColor = function seleccionarColor(otroColor){
        this.color = otroColor
    }

    this.verComoEs = function verComoEs(){
        var cadena = "Marca: " + this.marca + "<br />" +
        "Modelo: " + this.modelo +  "<br />" +
        "Color: " + this.color;
        return cadena
    }

}

Ahora, a esta altura de la página ya tenemos cargada en memoria una clase del objeto coche. En el código que está a continuación de estos párrafos vamos a crear una primera instancia de esa clase y la llamamos por ejemplo miPrimerCoche. Así con var miPrimerCoche = new coche("Volkswagen", "Golf", "Rojo"), donde se observa la palabra clave de JavaScript new, creamos una nueva instancia de la clase ya existente coche. Digamos que una instancia de una clase (entidad abstracta) es finalmente uno de sus indefinidos objetos (entidades concretas) que pueden crearse.

Con el método new coche(), que también podemos denominar como método constructor pues lo que hace es precisamente crear un nuevo objeto, le pasamos unas propiedades y ya tenemos en memoria este objeto. Luego usamos el método verComoEs() para extraer las propiedades de ese coche en concreto y comprobar que el objeto se haya creado. El código de esto va incluido en un elemento <script> pero inmediatamente antes ponemos un elemento <div> que nos servirá para exponer el volcado de las propiedades. Esté codigo está situado en esta página precisamente después de la frase "Así es mi primer coche", aunque volvemos a repetir que el script debería situarse en archivos externos.

<div id="div1"></div>
<script>
    var miPrimerCoche = new coche("Volkswagen", "Golf", "Rojo");
    document.getElementById("div1").innerHTML = miPrimerCoche.verComoEs();
</script>
Actualización NOV 2012: Con los últimos cambios he decidido que todos los scripts vinculados desde archivos externos se carguen asíncronamente. Por lo tanto el código del script anterior y el resto de esta página que necesiten el archivo objetos.js cargado, los he tenido que traspasar al window.onload de esta página, momento en el cual estará disponible el constructor ubicado en el archivo vinculado objetos.js.

Ejemplo:

"Así es mi primer coche"

Ahora quiero cambiar miPrimerCoche de color, a verde, por ejemplo:

<div id="div2"></div>
<script>
    miPrimerCoche.seleccionarColor("Verde");
    document.getElementById("div2").innerHTML = miPrimerCoche.verComoEs();
</script>
    

Ejemplo:

"Creo que mi primer coche me gusta más de color verde"

Vamos a "comprar" ahora otro nuevo coche:

<div id="div3"></div>
<script>
    var miSegundoCoche = new coche("Renault", "Clio", "Azul");
    document.getElementById("div3").innerHTML = miSegundoCoche.verComoEs();
</script>

Ejemplo:

"Así es mi segundo coche"

Pongamos los dos coches juntos:

<div id="div4"></div>
<script>
    var cadena = "El primero: <br />" + miPrimerCoche.verComoEs() + "<br />" +
        "Y el segundo: <br />" + miSegundoCoche.verComoEs();
    document.getElementById("div4").innerHTML = cadena;
</script>

Ejemplo:

"Mis dos coches"

Y en esta página podríamos seguir creando indefinidas instancias de la clase coche, con lo que queda patente la reutilización del mismo código para difentes valores.

Reescribiendo un objeto: la función modificadora de objetos

Decimos que un objeto se compone de propiedades y métodos. Inicialmente se establece una clase que es algo como una plantilla con la cual creamos los objetos, a los que denominamos instancias de esa clase. Cuando se diseña una clase se hace pensando en las múltiples instancias que pueden derivarse. De esta forma se incluyen en la clase todas las propiedades y métodos que pueden usar luego las instancias. Las propiedades y métodos que no sean genéricos de la clase pueden luego crearse directamente en el objeto.

Veámos esto con nuestra clase de ejemplo coche que hemos utilizado antes, recordamos que tenía las propiedades marca, modelo y color mientras que uno de sus métodos era verComoEs(). Ahora queremos seguir utilizando la clase coche para lo cual creamos una nueva instancia unCocheMatriculado pero usando una función intermedia que hace las veces de modificador de la clase pero actuando directamente sobre el objeto. Así esta función nos va a servir para agregarle dos nuevas propiedades matricula y fechaMatriculacion a la vez que le añadimos un nuevo método antiguedad(). Además vamos a sobreescribir el método de clase verComoEs(). El código de esta función modificadora es el siguiente, que también hemos situado en el archivo externo donde estaba la clase coche:

function crearCocheMatriculado(unaMarca, unModelo, unColor, unaMatricula, unaFechaMatriculacion) {
    var nuevoCoche = new coche(unaMarca, unModelo, unColor);
    nuevoCoche.matricula = unaMatricula;
    var fecha = unaFechaMatriculacion.split("/");
    nuevoCoche.fechaMatriculacion = new Date(fecha[2], (parseInt(fecha[1])-1), fecha[0]);
    nuevoCoche.calcularAntiguedad = function calcularAntiguedad(){
        var hoy = new Date()
        var anyos = (hoy - nuevoCoche.fechaMatriculacion)/(1000*60*60*24*365);
        anyos = anyos.toPrecision(2);
        return anyos + " años";
    }
    nuevoCoche.verComoEs = function verComoEs() {
        return "Marca: " + nuevoCoche.marca + "<br />" +
        "Modelo: " + nuevoCoche.modelo +  "<br />" +
        "Color: " + nuevoCoche.color +  "<br />" +
        "Matrícula: " + nuevoCoche.matricula +  "<br />" +
        "Fecha matriculación: " + nuevoCoche.fechaMatriculacion.getDate() + "/" +
        (parseInt(nuevoCoche.fechaMatriculacion.getMonth()) + 1) + "/" +
        nuevoCoche.fechaMatriculacion.getYear();
    }
    return nuevoCoche;
}
    

Así ahora en esta página podemos declarar una variable var unCocheMatriculado = crearCocheMatriculado("Seat", "León", "Amarillo", "ABC1234", "15/1/2007"); que llama a esa función crearCocheMatriculado() donde podemos apreciar:

El código para implantar en esta página los nuevos objetos es el siguiente:

<b><i>"Un coche..."</i></b>
<div id="div5"></div>
<b><i>"Este coche..."</i></b>
<div id="div6"></div>
<b><i>"¿Es este objeto..."</i></b>
<div id="div7"></div>
<b><i>"Este es..."</i></b>
<div id="div8"></div>
<b><i>"Este es un..."</i></b>
<div id="div9"></div>
<script>
    var unCocheMatriculado = crearCocheMatriculado("Seat", "León", "Amarillo", "ABC1234", "15/1/2007");
    document.getElementById("div5").innerHTML = unCocheMatriculado.verComoEs();
    document.getElementById("div6").innerHTML = unCocheMatriculado.calcularAntiguedad();
    document.getElementById("div7").innerHTML = unCocheMatriculado instanceof coche;
    var otroCocheMatriculado = crearCocheMatriculado("Mercedes", "350", "Rojo", "XYZ7899", "30/6/2000");
    document.getElementById("div8").innerHTML = otroCocheMatriculado.verComoEs();
	var cocheSinMatricula = new coche("Toyota", "Versus", "Magenta");
	document.getElementById("div9").innerHTML = cocheSinMatricula.verComoEs();
</script>

Este es el resultado. Observe que estos objetos son también instancias de la clase coche.

Ejemplo:

"Un coche matriculado, objeto unCocheMatriculado instanciado de la clase coche:"
"Este coche con matrícula tiene una antigüedad de:"
"¿Es este objeto unCocheMatriculado una instancia de la clase coche?:"
"Este es otroCocheMatriculado también de la clase coche:"
"Este es un cocheSinMatricula creado directamente de la clase coche sin usar la función modificadora crearCocheMatriculado():"

Esta característica de reescritura de los objetos es muy importante, pues nos permite no tener que incluir en los métodos de clase las especificidades, sino las generalidades de las instanciaciones que podamos hacer de esa clase. Así podemos reutilizar el programa o código de la clase para un mayor número de objetos instanciados, con la seguridad de que luego podemos gestionar esas cosas específicas con el añadido de propiedades y/o métodos así como la reescritura de éstos. Sin embargo esta reescritura sólo afecta al objeto instanciado, aunque usando la función modificadora de objetos de antes podemos crear cuantos coches matriculados queramos, tal como hicimos en el ejemplo que instanciamos dos coches sin necesidad de volver a escribir el código de la clase o de la función modificadora.


Con lo explicado hasta aquí hay suficiente para empezar a manipular objetos y de hecho nuestro calendario no necesita más, por lo que puede pasar directamente a ver el calendario. En otro caso quizás le interese conocer más de los objetos de JavaScript en el siguiente capítulo.