Mejorando la clase de ejemplo coche

Hasta ahora hemos visto que una clase es una plantilla para crear instancias que denominamos finalmente objetos. Comentamos que una vez creado un objeto se le pueden modificar sus propiedades y/o métodos. Vimos que las clases disponen de un prototipo mediante el cual podemos modificar la clase (la plantilla), tal que esos cambios se reflejan en todas las instancias existentes. Si sólo hemos de manejar pocos objetos (o instancias) de una clase el problema podría manejarse con lo visto hasta ahora. Pero en otros casos la complejidad requerirá otras soluciones.

Empecemos tomando la clase coche vista en temas anteriores y aplicando algunas mejoras antes de pasar al concepto de herencia. Supongamos que aquel concesionario que sólo vendía turismos ahora decide ampliar su negocio a todo tipo de vehículos usados (camiones, autobuses, etc.). La clase coche que habíamos definido antes ahora la llamamos vehiculo. Los argumentos unaMarca, unModelo, unColor y unaMatricula son del tipo String, mientras que unaFechaMatriculación será recibido del tipo Date.

function vehiculo(unaMarca, unModelo, unColor, unaMatricula, unaFechaMatriculacion) {
    this.marca = new propiedad("Marca", unaMarca, "String");
    this.modelo = new propiedad("Modelo", unModelo, "String");
    this.color = new propiedad("Color", unColor, "String");
    this.matricula = new propiedad("Matrícula", unaMatricula, "String");
    this.fechaMatriculacion = new propiedad("Fecha matriculación", unaFechaMatriculacion, "Date");

    this.calcularAntiguedad = function calcularAntiguedad(){
        var cad = "No hay fecha de matriculación.";
        if (this.fechaMatriculacion != null){
            var hoy = new Date();
            var anyos = (hoy - this.fechaMatriculacion.valor)/(1000*60*60*24*365);
            anyos = anyos.toPrecision(2);
            cad = anyos + " años";
        }
        return cad;
    }

    this.verComoEs = function verComoEs(){
        var cad = "";
        for (unaPropiedad in this){
            if (typeof(this[unaPropiedad]) != "function"){
                var val = this[unaPropiedad].valor;
                if (this[unaPropiedad].tipo == "Date"){
                    val = val.getDate() + "/" + val.getMonth() + "/" + val.getFullYear();
                }
                cad += this[unaPropiedad].titulo + " = " + val + "<br />";
            }
        }
        return cad;
    }


}
    
Anteriormente tenía la función getYear() pero he detectado que con Explorer devolvía correctamente el año en este ejemplo, pero no así con los otros navegadores (en lugar de 2005 devolvía 105). Parece que está declarada obsoleta y debe usarse getFullYear() que devuelve el año completo.

Creamos también una pequeña clase propiedad que define un objeto para insertar las propiedades dentro de la clase. Así cada propiedad se compone de un titulo, un valor y un tipo. El título será el literal que presentaremos cuando queramos extraer el valor del campo. El tipo se refiere al tipo de datos, lo que nos permitirá manejarlo adecuadamente. De esta forma en las propiedades de la clase vehiculo insertamos objetos propiedad en lugar de valores, por ejemplo this.marca = new propiedad("Marca", unaMarca, "String").

function propiedad(unTitulo, unValor, unTipo){
    this.titulo = unTitulo;
    this.valor = unValor;
    this.tipo = unTipo;

}
    

En el método calcularAntiguedad() puede ver como accedemos a la fechaMatriculacion a través de nuestro objeto propiedad poniendo this.fechaMatriculacion.valor con lo que obtenemos el valor de la fecha.

El método verComoEs() también lo hemos mejorado. En lugar de hacer una recopilación "a mano" de las propiedades de la clase, lo que hacemos es usar for (unaPropiedad in this) para iterar por todas las propiedades y métodos. Luego filtramos quitando los métodos que serán del tipo function. El resto (y esto es responsabilidad del programador) deberán ser un objeto de nuestra nueva clase propiedad. Vemos que aquí podemos formatear los valores de fecha usando el tipo de propiedad. Observe que para acceder a una propiedad de un objeto podemos hacer objeto.nombre_propiedad o bien objeto["nombre_propiedad"], usando esta última forma porque en este caso es un literal del nombre de la propiedad.

Con esta clase podemos crear un coche como el siguiente. Observe como le pasamos el argumento fecha en formato de objeto Date(año, mes+1, día), donde el el año es un entero de cuatro dígitos, el mes está en el rango [0..11], (por eso le sumamos 1), mientras que los días se toman en el rango [1..31].

<b><i>"Así es el objeto...:"</i></b>
<div id="div1"></div>
<b><i>"Y esta es su antiguedad:"</i></b>
<div id="div10"></div>
<script>
        var unVehiculo = new vehiculo("Volkswagen", "Golf", "Rojo", "ABC1234",
        new Date(2005, 6+1, 15));
        document.getElementById("div1").innerHTML = unVehiculo.verComoEs()
        document.getElementById("div10").innerHTML = unVehiculo.calcularAntiguedad();
</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 lo he tenido que traspasar al window.onload de esta página, momento en el cual estará disponible el constructor ubicado en el archivo vinculado objeto2.js.

En este resultado observamos la utilidad del objeto propiedad, lo que nos permite poner los títulos adecuados a cada valor de propiedad sin tener que hacer una relación "a mano" de ellas en el método verComoEs(). En definitiva esta nueva clase vehiculo viene a ser (y a hacer) lo mismo que la anterior coche, aunque con algunas mejoras, pero nos servirá de base para seguir trabajando.

Ejemplo:

"Así es el objeto unVehiculo de la clase vehiculo:"
"Y esta es su antiguedad:"

Identificando los objetos: diseño de la jerarquía de clases

Si ahora queremos registrar un vehículo pesado, un camión por ejemplo, podría interesarnos una propiedad específica como el peso máximo autorizado, propiedad que no costaría nada incluirla en la clase anterior, pero que podría no ser relevante para un turismo. Si fuera un autobús quizás lo que nos interese es el número de viajeros que puede transportar, dato que no precisa un camión. Además podría haber métodos específicos para un camión, que no serían de aplicación a un turismo o autobús. Si sólo se trata de unas pocas propiedades y métodos el problema no es grave. Pero la cosa podría complicarse. En estos casos conviene dedicar un tiempo previo para identificar a los objetos.

  • Identificar los objetos reales, sus propiedades y métodos. Haríamos un desglose detallado de los posibles objetos que podrían manejarse y de sus propiedades, como por ejemplo:
    Objeto realPropiedadesMétodos
    TurismoMarca, modelo, color, matrícula, fecha de matriculación, combustible, potenciaMostrar sus propiedades, calcular antigüedad
    CamiónMarca, modelo, color, matrícula, fecha de matriculación, peso en vacío, peso máximo autorizadoMostrar sus propiedades, calcular antigüedad, calcular carga máxima
    AutobúsMarca, modelo, color, matrícula, fecha de matriculación, número de viajerosMostrar sus propiedades, calcular antigüedad
    etcétera......
  • Extraer analogías entre los objetos, es decir, ver que propiedades y métodos son comunes y por tanto se pueden compartir y cuáles no.

Es en este segundo punto donde aplicaríamos el concepto de herencia. Aquellas propiedades y métodos que se comparten por todos los objetos habrían de incluirse en una clase base, a veces llamada superclase. Para el resto se crean las nuevas subclases que heredarán las propiedades y métodos comunes de la clase base o superclase.

A continuación podríamos volver a refinar la estructura con una nueva subdivisión. Por ejemplo, los turismos podrían ser familiares, utilitarios o deportivos. Cada una de estas subclases podrían tener propiedades y métodos específicos.

Composición de objetos frente a herencia de objetos

Identificar los objetos es una tarea que requiere un mayor aprendizaje del expuesto aquí y, por supuesto, de más experiencia. Pero podemos apuntar alguna cosa que puede ayudar. Supongamos que creamos una clase llamada motor que incluye propiedades características como cilindrada, potencia, encendido, etc. Cada clase vehiculo o sus subclases estarán relacionadas de alguna forma con algún objeto motor pues todo vehículo tiene un motor (en nuestro caso). La respuesta es que ni vehiculo heredará nada de motor ni al revés, es decir, no existe ninguna jerarquía implicada aquí. La clave de este asunto está en preguntarse lo siguiente:

  • ¿Un turismo ES UN vehiculo?, ¿Un camion ES UN vehiculo?, ¿Un autobus ES UN vehiculo?. Si la respuesta es SI es que se trata de subclases que heredarán de la superclase, es decir, hay implícita una jerarquía de clases.
  • ¿Un turismo TIENE un motor?, etc. Si la respuesta es SI entonces es que es una simple composición de objetos y no hay jerarquía de clases.

Vemos que ambas preguntas no pueden responderse afirmativamente al mismo tiempo. Un turismo es un vehículo, pero un vehículo no tiene un turismo. En cambio un vehículo tiene un motor, pero no es un motor.

Es algo parecido a lo que hicimos con nuestro objeto propiedad insertando los objetos de esta clase dentro de vehiculo. Podemos preguntarnos ¿un vehiculo TIENE una propiedad?, cuya respuesta es SI, pues ni un vehiculo ES una propiedad ni al revés. Que no exista jerarquía de clases, o lo que es lo mismo, que no se de una relación de herencia, no quiere decir que no haya un tipo de relación, pues precisamente esta es una relación de composición de objetos.

Definiendo las subclases del ejemplo vehiculo

De la sencillez del ejemplo se observa que nuestra clase vehiculo expuesta antes contiene las propiedades y métodos de la clase base. El resto son propias de nuevas clases que hemos de crear: turismo, camion, autobús etc., aunque sólo vamos a crear las dos primeras pues es suficiente para probar el ejemplo:

nivelnombrepropiedadesmétodos
clasevehiculomarca, modelo, color, matricula, fechaMatriculacioncalcularAntiguedad(), verComoEs()
subclaseturismocombustible, potencia
subclasecamionpesoVacio, pesoMaximoAutorizadocalcularCargaMaxima()

En base a esa primera clasificación (nunca mejor dicho pues de eso se trata, agrupar o disponer por clases), pasamos a codificar los algoritmos. Ya teníamos la clase base vehiculo tal como figura más arriba. Luego crearemos turismo y camion para terminar de documentar el ejemplo. Pero antes hay que saber como hacerlo.

Composición de objetos

Para aclarar los conceptos, en lugar de usar nuestra jerarquía de clases anterior, usaremos objetos más simples y al final volveremos al ejemplo anterior. Para este y los siguientes ejemplos declaramos una clase que denominamos superClase:

<script>
    function superClase(argumento) {
        this.valorSuper = argumento;
        this.metodoSuper = function metodoSuper(){
            return "\"ESTE MÉTODO NO HACE NADA\".";
        }
    }
    </script>
    

Luego declaramos otra clase denominada subClase1 y hacemos una composición de ésta con la anterior superClase. En los comentarios del código puede ver más detalles:

<b><i>"Composición de objetos:"</i></b>
<div id="div3"></div>
<script>
    //Declaramos una subClase1
    function subClase1(argumentoSub, argumentoSuper){
        this.valorSub = argumentoSub;
        //Componemos un objeto de la super clase en la sub clase
        this.objetoSuper = new superClase(argumentoSuper);
    }
    //Creamos un objeto de la subClase1
    var objetoSub1 = new subClase1("\"VALOR-SUB-1\"", "\"VALOR-SUPER-1\"");
    //Comprobamos instancias
    var cad = "¿Es <code>objetoSub1.objetoSuper</code> una instancia de <code>superClase</code>?: " +
    (objetoSub1.objetoSuper instanceof superClase) + "<br />" +
    "¿Es <code>objetoSub1</code> una instancia de <code>superClase</code>?: " +
    (objetoSub1 instanceof superClase) + "<br />";
    //Comprobamos que objetoSub1 no posee la propiedad "valor"
    cad += "¿Tiene <code>objetoSub1</code> la propiedad <code>valorSuper</code>?: " +
    (objetoSub1.hasOwnProperty("valorSuper")) + "<br />";
    //Comprobamos que objetoSub1.objetoSuper si posee la propiedad "valor"
    cad += "¿Tiene <code>objetoSub1.objetoSuper</code> la propiedad <code>valorSuper</code>?: " +
    (objetoSub1.objetoSuper.hasOwnProperty("valorSuper")) + "<br />";
    //Presentamos el valor y el método del objeto insertado
    cad += "Esto es <code>objetoSub1.objetoSuper.valorSuper</code>: " +
    objetoSub1.objetoSuper.valorSuper + "<br />" +
    "Además podemos aplicar <code>unObjeto.otroObjeto.metodoSuper()</code>: " +
    objetoSub1.objetoSuper.metodoSuper();
    //Presentamos todo 
    document.getElementById("div3").innerHTML = cad;
    </script>
    

Ejemplo:

"Composición de objetos:"

Insertar un objeto dentro de otro como una de sus propiedades no es herencia sino composición de objetos, pues cuando hacemos objetoSub1.objetoSuper.valorSuper realmente no estamos heredando la propiedad valorSuper de superClase, sino que estamos accediendo a la propiedad valorSuper de la propiedad objetoSuper que posee objetoSub1. En el resultado anterior observamos que el nuevo objeto insertado es una instancia de superClase y que su propiedad valorSuper sigue estando en su poder, es decir, hay que poner objetoSub1.objetoSuper.valorSuper para acceder a ella.

Sin embargo objetoSub1 NO es una instancia de superClase y esto significa que objetoSub1 no hereda de superClase, como veremos a continuación con los mecanismos de herencia.

Herencia mediante enmascaramiento de objeto (object masquerading)

Hay otra forma de incluir en una clase una referencia a otra externa. En inglés suele expresarse con object masquerading, que se puede traducir como enmascaramiento de objeto y que al final de este apartado entenderemos porqué se le llama así. Se trata de declarar la referencia al nombre de una clase externa, lo que hacemos en la declaración de la sub clase (que ahora llamamos subClase2 para diferenciarla de la anterior en esta misma página), poniendo this.objetoSuper = superClase. En este momento la propiedad objetoSuper apunta a la clase superClase pero no hay ningún objeto ahí. Por ahora es sólo una referencia, un puntero a un lugar de memoria. Luego, en lugar de usar el operador new para construir el objeto externo, usamos this.objetoSuper(argumentoSuper) con lo que todas las propiedades y métodos de la clase externa superClase pasan a formar parte de subClase2.

<b><i>"Mecanismo de herencia:"</i></b>
<div id="div4"></div>
<script>
    //Declaramos una subClase2
    function subClase2(argumentoSub, argumentoSuper){
        this.valorSub = argumentoSub;
        //Enmascaramiento del objetoSuper
        this.objetoSuper = superClase;
        this.objetoSuper(argumentoSuper);
    }
    //Creamos un objeto de la sub clase
    var objetoSub2 = new subClase2("\"VALOR-SUB-2\"", "\"VALOR-SUPER-2\"");
    //Comprobamos instancias
    var cad = "¿Es <code>objetoSub2.objetoSuper</code> una instancia de <code>superClase</code>?: " +
    (objetoSub2.objetoSuper instanceof superClase) + "<br />";
    //Comprobamos si posee la propiedad "valor"
    cad += "¿Tiene <code>objetoSub2</code> la propiedad <code>valorSuper</code>?: " +
    (objetoSub2.hasOwnProperty("valorSuper")) + "<br />";
    //Comprobamos que NO existe objetoSub2.objetoSuper.valorSuper
    cad += "¿Tiene <code>objetoSub2.objetoSuper</code> la propiedad <code>valorSuper</code>?: " +
    (objetoSub2.objetoSuper.hasOwnProperty("valorSuper")) + "<br />";
    //Presentamos el valor y el método del objeto insertado
    cad += "En la propiedad <code>objetoSub2.valorSuper</code> tenemos el valor: " +
    objetoSub2.valorSuper + "<br />" +
    "y también podemos extraer <code>objetoSub2.metodoSuper()</code>: " +
    objetoSub2.metodoSuper() + "<br />";
    //Ahora modificamos la propiedad y reescribimos el método
    objetoSub2.valorSuper = "\"OTRO VALOR DE SUPER-2\"";
    cad += "Podemos modificar <code>objetoSub2.valorSuper</code>: " +
    objetoSub2.valorSuper + "<br />";
    objetoSub2.metodoSuper = function metodoSuper(){
        return "\"HEMOS REESCRITO ESTE MÉTODO DE SUPER-2.\""
    }
    cad += "y también podemos reescribir <code>objetoSub2.metodoSuper()</code>:" +
    objetoSub2.metodoSuper() + "<br />";
    //Presentamos todo 
    document.getElementById("div4").innerHTML = cad;
    </script>

    

Ejemplo:

"Mecanismo de herencia por enmascaramiento de objeto:"

Lo primero que observamos en el resultado es que el objeto insertado (o enmascarado) objetoSub2.objetoSuper ya no es una instancia de la clase superClase. Por otro lado objetoSub2 de subClase posee la propiedad valorSuper, mientras que el objeto insertado ya no la posee. En cuanto a esto hemos de comentar que el método hasOwnProperty("nombre_propiedad") (que se hereda del constructor Object a través de function) nos dice si un objeto es propietario de cierta propiedad, pero no chequea aquellas que se incorporen como herencia. Realmente el propietario sería la super clase. Por lo tanto antes debería dar false. Si probamos con la propiedad valorSub de la cual objetoSub es su propietario, veremos que nos da true ejecutando el código objetoSub2.hasOwnProperty("valorSub"):

Ejemplo:

Por último vemos que el objeto insertado (o enmascarado) objetoSub2.objetoSuper viene a comportarse como una referencia intermedia a la que se les quita la posesión de sus propiedades. ¿O no es así?. Si intentamos acceder a ellas veremos que podemos aún modificarlas:

<b><i>"Aún podemos acceder al objeto insertado:"</i></b>
<div id="div4-2"></div>
<script>
    //Usando lo anterior, comprobamos que podemos acceder aún al objeto insertado
    objetoSub2.objetoSuper.valorSuper = "\"VOLVEMOS A MODIFICAR EL VALOR DE SUPER-2\"";
    cad = "Aún podemos acceder a <code>objetoSub2.objetoSuper.valorSuper</code> y modificarlo: " +
    objetoSub2.objetoSuper.valorSuper + "<br />" +
    "y por otro lado seguimos conservando <code>objetoSub2.valorSuper</code> anterior: " +
    objetoSub2.valorSuper;
    //Presentamos todo
    document.getElementById("div4-2").innerHTML = cad;
    </script>

    

Ejemplo:

"Aún podemos acceder al objeto insertado:"

Esto puede dar lugar a malentendidos y realmente lo que nos interesa es objetoSub2.valorSuper y no objetoSub2.objetoSuper.valorSuper. Al fin y al cabo la propiedad objetoSuper no es más que una referencia intermedia o temporal que sólo sirve para llevar a cabo este mecanismo de herencia, por lo que usamos el operador delete para borrar esa referencia y así no acceder a ese objeto. Declaramos una nueva sub clase que denominamos subClase3 para no interferir con la anterior:

<b><i>"Mecanismo de herencia completado:"</i></b>
<div id="div5"></div>
<script>
    //Declaramos una subClase3
    function subClase3(argumentoSub, argumentoSuper){
        //Primero aplicamos herencia desde la super clase y borramos
        //el objeto temporal o enmascarado
        this.objetoSuper = superClase;
        this.objetoSuper(argumentoSuper);
        delete this.objetoSuper;
        //A continuación ponemos las propiedades y/o métodos de la sub clase
        this.valorSub = argumentoSub;
    }
    //Creamos un objeto de la sub clase
    var objetoSub3 = new subClase3("\"VALOR-SUB-3\"", "\"VALOR-SUPER-3\"");
    //Comprobamos propiedades y métodos heredados desde la super clase
    cad = "¿Tiene <code>objetoSub3</code> la propiedad <code>valorSuper</code>?: " +
    (objetoSub3.hasOwnProperty("valorSuper")) + "<br />" +
    "En la propiedad <code>objetoSub3.valorSuper</code> tenemos el valor: " +
    objetoSub3.valorSuper + "<br />" +
    "y también podemos extraer <code>objetoSub3.metodoSuper()</code>: " +
    objetoSub3.metodoSuper() + "<br />";
    //Ahora modificamos la propiedad y reescribimos el método
    objetoSub3.valorSuper = "\"ESTE ES UN NUEVO VALOR DE SUPER-3\"";
    cad += "Podemos modificar <code>objetoSub3.valorSuper</code>: " +
    objetoSub3.valorSuper + "<br />";
    objetoSub3.metodoSuper = function metodoSuper(){
        return "\"ESTO ES REESCRITO DEL MÉTODO DE SUPER-3.\""
    }
    cad += "y también podemos reescribir <code>objetoSub2.metodoSuper()</code>:" +
    objetoSub3.metodoSuper() + "<br />";
    //Presentamos todo
    document.getElementById("div5").innerHTML = cad;
    </script>	
	

Ejemplo:

"Mecanismo de herencia completado:"

Ahora podeemos entender que esto es un mecanismo de herencia haciendo uso de object masquerading, es decir, mediante un enmascaramiento del objeto insertado que usamos temporalmente y luego eliminamos.

Detectando la herencia con el operador instanceof

Aparte de lo que vimos acerca de que el método hasOwnProperty() daba cierto para la propiedades heredadas cuando debería dar falso, otro problema que tiene ese mecanismo anterior es que el operador instanceof no funciona adecuadamente, puesto que objetoSub3 SI es una instancia de superClase, pero no lo detecta.

Ejemplo:

Una forma de arreglar todo esto es usando los prototipos que ya vimos en un tema anterior.

Uso de prototype para implantar la herencia (prototype chaining)

En el tema anterior vimos como usar el prototipo de una clase para modificar su plantilla con la propiedad prototype. Ahora volvemos a usarlo para el propósito de crear un mecanismo de herencia que en inglés suele expresarse como prototype chaining, lo que se puede traducir como mecanismo de herencia mediante encadenamiento del prototipo.

<b><i>"Mecanismo de herencia con <code>prototype</code>:"</i></b>
<div id="div6"></div>
<script>
    //Declaramos una subClase4 pero SÓLO CON SUS ARGUMENTOS
    function subClase4(argumentoSub){
        this.valorSub = argumentoSub;
    }
    //Accedemos a su prototipo y agregamos la super clase con SUS ARGUMENTOS
    subClase4.prototype = new superClase("\"VALOR-SUPER-4\"");
    //Creamos un objeto de la sub clase con SUS ARGUMENTOS
    var objetoSub4 = new subClase4("\"VALOR-SUB-4\"");
    //Comprobamos que es instancia del objeto de donde hereda
    cad = "Ahora <code>objetoSub4</code> SI es una instancia de <code>superClase</code>: " +
    (objetoSub4 instanceof superClase) + "<br />";
    //Comprobamos propiedades y métodos heredados desde la super clase
    cad += "¿Tiene <code>objetoSub4</code> la propiedad <code>valorSuper</code>?: " +
    (objetoSub4.hasOwnProperty("valorSuper")) + "<br />" +
    "¿Tiene <code>objetoSub4</code> la propiedad <code>valorSub</code>?: " +
    (objetoSub4.hasOwnProperty("valorSub")) + "<br />" +
    "En la propiedad <code>objetoSub4.valorSuper</code> tenemos el valor: " +
    objetoSub4.valorSuper + "<br />" +
    "y también podemos extraer <code>objetoSub4.metodoSuper()</code>: " +
    objetoSub4.metodoSuper() + "<br />";
    //Ahora modificamos la propiedad y reescribimos el método
    objetoSub4.valorSuper = "\"VALOR DE SUPER-4 MODIFICADO\"";
    cad += "Podemos modificar <code>objetoSub4.valorSuper</code>: " +
    objetoSub4.valorSuper + "<br />";
    objetoSub4.metodoSuper = function metodoSuper(){
        return "\"MÉTODO DE SUPER-4 MODIFICADO.\""
    }
    cad += "y también podemos reescribir <code>objetoSub2.metodoSuper()</code>:" +
    objetoSub4.metodoSuper() + "<br />";
    //Presentamos todo
    document.getElementById("div6").innerHTML = cad;
    </script>
    

Ejemplo:

"Mecanismo de herencia con prototype:"

Ahora objetoSub4 SI es instancia de superClase y además el método hasOwnProperty() sobre la propiedad valorSuper nos da falso como era de esperar en un mecanismo de herencia más completo. Pero este mecanismo tiene el inconveniente de que no podemos crear una subClase con todos los argumentos, los propios de la sub clase y los de la super clase, tal como hicimos con el mecanismo por enmascaramiento anterior.

Aplicando herencia a nuestra jerarquía de clases vehiculo

Volvemos a nuestra super clase vehiculo que será heredada por las subclases turismo y camion. Para la primera haremos un implantación de herencia con object masquerading, mientras que para la segunda subclase usaremos prototype chaining. Este es el código de la subclase turismo:

function turismo(unaMarca, unModelo, unColor, unaMatricula, unaFechaMatriculacion,
        unCombustible, unaPotencia){
    this.temporal = vehiculo;
    this.temporal(unaMarca, unModelo, unColor, unaMatricula, unaFechaMatriculacion);
    delete this.temporal;
    this.combustible = new propiedad("Combustible", unCombustible, "String");
    this.potencia = new propiedad("Potencia", unaPotencia, "String");

}
    

Y esta es una aplicación práctica que crea una instancia de esa subclase turismo heredando propiedades y métodos de la super clase vehiculo con el mecanismo de enmascaramiento:

<b><i>"Un turismo que hereda por enmascaramiento de la super clase <code>vehiculo</code>"</i></b>
<div id="div7"></div>
<script>
    var unTurismo = new turismo("Volkswagen", "Golf", "Rojo", "ABC1234",
            new Date(2005, 6+1, 15), "Gasolina", "70CV");
    document.getElementById("div7").innerHTML =
    "Método <code>verComoEs()</code> de la super clase: <br />" +
    unTurismo.verComoEs() + "<br />" +
    "Método <code>calcularAntiguedad()</code> de la superclase: " +
    unTurismo.calcularAntiguedad() + "<br />" +
    "Propiedad <code>color</code> de la superclase, con título y valor: " +
    unTurismo.color.titulo + "=" + unTurismo.color.valor + "<br />" +
    "Propiedad <code>potencia</code> de la subclase, con título y valor: " +
    unTurismo.potencia.titulo + "=" + unTurismo.potencia.valor;
</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 lo he tenido que traspasar al window.onload de esta página, momento en el cual estará disponible el constructor ubicado en el archivo vinculado objeto2.js.

Ejemplo:

"Un turismo que hereda por enmascaramiento la super clase vehiculo"

A continuación ponemos la declaración de la subclase camion. Aquí no hay ningún mecanismo de herencia y es simplemente una declaración de clase:

function camion(unPesoVacio, unPesoMaximoAutorizado){
    this.pesoVacio = new propiedad("Peso vacío", unPesoVacio, "Number");
    this.pesoMaximoAutorizado = new propiedad("Peso máximo autorizado", unPesoMaximoAutorizado, "Number");
    this.calcularCargaMaxima = function calcularCargaMaxima(){
        return this.pesoMaximoAutorizado.valor - this.pesoVacio.valor;
    }
}
    

Y esta es la aplicación práctica que crea una instancia de camion, pero donde también implantamos ese mecanismo de herencia mediante el prototipo:

<b><i>"Un camión que hereda por prototipo la super clase <code>vehiculo</code>"</i></b>
<div id="div8"></div>
<script>
    camion.prototype = new vehiculo("Fiat", "M50", "Verde", "XYZ4789",
            new Date(2007, 3+1, 17));
    var unCamion = new camion(2000, 5500);
    document.getElementById("div8").innerHTML =
    "Método <code>verComoEs()</code> de la super clase: <br />" +
    unCamion.verComoEs() + "<br />" +
    "Método <code>calcularAntiguedad()</code> de la super clase: " +
    unCamion.calcularAntiguedad() + "<br />" +
    "Propiedad <code>color</code> de la superclase, con título y valor: " +
    unCamion.color.titulo + "=" + unCamion.color.valor + "<br />" +
    "Propiedad <code>pesoMaximoAutorizado</code> de la subclase, con título y valor: " +
    unCamion.pesoMaximoAutorizado.titulo + "=" + unCamion.pesoMaximoAutorizado.valor + "<br />" +
    "Propiedad <code>pesoVacio</code> de la subclase, con título y valor: " +
    unCamion.pesoVacio.titulo + "=" + unCamion.pesoVacio.valor + "<br />" +
    "Método <code>calcularCargaMaxima()</code> de la subclase: " +
    unCamion.calcularCargaMaxima();
</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 lo he tenido que traspasar al window.onload de esta página, momento en el cual estará disponible el constructor ubicado en el archivo vinculado objeto2.js.

Ejemplo:

"Un camión que hereda por prototipo la super clase vehiculo"

Habíamos dicho que una desventaja del mecanismo del prototipo era el tema de los argumentos que había que pasar, en su caso, a la subclase y superclase de forma no conjunta. Para facilitar esta tarea podemos crear una función intermedia que nos cree la instancia de la subclase al tiempo que incorpora el mecanismo de herencia por prototipo y conseguir pasar todos los argumentos de una vez:

function crearCamion(marca, modelo, color, matricula, fechaMatricula,
        pesoVacio, pesoMaximoAutorizado) {
    camion.prototype = new vehiculo(marca, modelo, color, matricula,
        fechaMatricula);
    var temporal = new camion(pesoVacio, pesoMaximoAutorizado);
    return temporal;
}

Así el código anterior puede estar situado en un sitio aparte (en un archivo externo) para un uso compartido y cuando necesitemos instanciar un objeto de la subclase camion la invocamos:

...
<script>
    var otroCamion = crearCamion("Fiat", "M50", "Verde", "XYZ4789",
            new Date(2007, 3+1, 17), 2000, 5500);
    ... (lo demás es igual que el código anterior) .......

</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 lo he tenido que traspasar al window.onload de esta página, momento en el cual estará disponible el constructor ubicado en el archivo vinculado objeto2.js.

Obtenemos el mismo resultado que antes:

Ejemplo:

"Otro camión que hereda por prototipo y con una función creadora de instancias"

Recapitulación

En estos cuatro temas se ha intentado ofrecer una panorámica básica sobre el universo de los objetos en programación JavaScript. Pero es sólo la "punta del iceberg" en esta materia, pues vea por ejemplo esta página de Wikipedia sobre características de la Programación Orientada a Objetos (POO). Algunas de ellas se han aplicado de alguna manera, como la abstracción o encapsulamiento cuando hablabámos de la jerarquía de clases. Otras como polimorfismo o la herencia múltiple (que es posible con JavaScript), ni las hemos mencionado.


El siguiente y último capítulo se dedica a realizar una aplicación práctica con objetos: Cómo se hace un calendario con objetos de JavaScript.