• 10 hours
  • Easy

Free online content available in this course.

course.header.alt.is_certifying

Got it!

Last updated on 6/12/20

Objetos II

Log in or subscribe for free to enjoy all this course has to offer!

 

Acabas de aprender a crear tus primeros objetos en JavaScript. Este capítulo os permitirá conocer mejor su funcionamiento y descubrir ciertas posibilidades que la programación orientada a objetos ofrece.

Juego de rol, versión extendida

En el capítulo anterior hemos creado un personaje que tiene una serie de propiedades o características y puede realizar una acción.

var personaje2 = {
    nombre: "Mago",
    vida: 150,
    fuerza: 12,

    // Retorna la descripción del personaje
    describir: function () {
        var descripcion = this.nombre + " tiene " 
                        + this.vida + " puntos de vida y " 
                        + this.fuerza + " de fuerza";
        return descripcion;
    }

};

Para hacer que el juego tenga un poco más de sentido, es necesario añadir otros personajes para que puedan interactuar entre ellos. Para esto, podemos crear un segundo objeto personaje2 y otorgarle unas propiedades y métodos.

Enseguida puedes darte cuenta de que si creamos otro objeto,  personaje2  como hemos hecho en el ejemplo de arriba, estamos repitiendo código, ya que sus características son casi idénticas. Tienen las mismas propiedades (nombre, salud, fuerza y capacidad de describir su estado) pero con valores diferentes. Esto debe servirnos como indicativo de que hay otras formas mejores de hacerlo.

Ya sabes que la duplicación de código es uno de nuestros principales enemigos. Convendría realizar un objeto modelo que represente a un personaje, y a partir de este modelo crear otros personajes. Veamos cómo hacerlo.

Objetos y prototipos en JavaScript

Los modelos a partir de los cuales se crean otros objetos, obteniendo sus propiedades y métodos, es a lo que en programación se conoce como herencia.

Para crear modelos de objeto en JavaScript, utilizamos los prototipos. Además de las propiedades particulares que cualquier objeto puede poseer (por ejemplo, vida, fuerza o nombre), todo objeto de JavaScript está ligado a un prototipo a partir del cual ha sido creado. Se trata pues, de un vínculo (llamado referencia para ser precisos) al objeto prototipo, del que se toman diferentes propiedades y métodos. Si se intenta acceder a una propiedad que no existe en ese objeto, JavaScript buscará esa propiedad en el prototipo del objeto. Esto se conoce como la cadena de prototipos.

Veamos un ejemplo que ilustre el funcionamiento de los objetos y su prototipo en JavaScript.

var objeto1 = {
    nombre: "nombre del objeto 1"
};

// Creamos objeto2 cuyo prototipo será el objeto1
var objeto2 = Object.create(objeto1);

// objeto2.nombre no es una propiedad del objeto2 pero
// sí está presente en su prototipo (objeto1)
console.log(objeto2.nombre); // retorna el nombre del prototipo

console.log(objeto2);

Creamos un nuevo objeto,  objeto1 que va a ser el modelo a partir del cual crear otros objetos. Le asignamos la propiedad nombre y creamos otro objeto,  objeto2  a partir de él. La instrucción  Object.create() se utiliza para crear un objeto ( objeto2 ) tomando como prototipo al objeto1, que se pasa como parámetro. El prototipo del objeto2 tendrá como referencia al objeto1, por lo que dispondrá de todos sus propiedades y métodos.

Si llamamos a  objeto2.nombre nos mostrará el nombre que definimos en su prototipo, ya que objeto2 no dispone de una propiedad nombre.

El resultado es el siguiente:

 

Mostramos objeto2 por consola
Mostramos objeto2 por consola

 

Si el prototipo de un objeto no posee una propiedad, JavaScript busca si esta propiedad se encuentra disponible en su prototipo, hasta llegar al final de la cadena de protipos. Si la propiedad no se encuentra ni en el objeto ni en sus prototipos, se retorna el valor  undefined .

Veamos el mismo ejemplo, pero ahora intentaremos acceder a una propiedad que no se encuentra ni en el objeto, ni en su prototipo:

var objeto1 = {
    nombre: "nombre del objeto 1"
};

// Creamos objeto2 a partir del prototipo objeto1
var objeto2 = Object.create(objeto1);

console.log(objeto2.nombre); // retorna el nombre del prototipo
console.log(objeto2.vida); // retorna undefined

console.log(objeto2);

 La propiedad  objeto2.vida  no está presente ni en objeto2, ni en su prototipo, por lo que el valor retornado es  undefined .

Resultado al acceder a una propiedad no disponible ni en el objeto ni en el prototipo
Resultado al acceder a una propiedad no disponible ni en el objeto ni en el prototipo

 

¿Qué pasaría si asignásemos  una propiedad nombre al objeto2? 

var objeto1 = {
    nombre: "nombre del objeto 1"
};

// Creamos objeto2 a partir del prototipo objeto1
var objeto2 = Object.create(objeto1);
// asignamos una propiedad nombre al objeto2
objeto2.nombre = "Objeto2";

console.log(objeto2.nombre); // retorna el nombre asignado al objeto2
console.log(objeto2.vida); // retorna undefined

console.log(objeto2);

Asignamos un nombre al objeto2 en la línea 10 y mostramos el resultado de  objeto2.nombre  por consola. El resultado es el siguiente:

 

La propiedad nombre ahora existe en objeto2
La propiedad nombre ahora existe en objeto2

JavaScript busca el valor de la propiedad nombre, y al encontrarlo en primer lugar, como una propiedad de objeto2, se detiene la búsqueda en la cadena de prototipos. Aunque el nombre del prototipo está también presente, la búsqueda no continúa, y retorna el valor encontrado en el propio objeto2, que tiene prioridad.

 

Utilicemos los prototipos en nuestro juego

Creación de personajes

Ahora que entendemos el concepto de objeto que sirve de patrón o prototipo a partir del cual podemos crear nuevos objetos, apliquémoslo a los personajes del juego. 

var Personaje = {
    nombre: "",
    vida: 100,
    fuerza: 100,
    xp: 0,

    // Retorna la descripción del personaje
    describir: function () {
        var descripcion = this.nombre + " tiene " 
                        + this.vida + " puntos de vida y " 
                        + this.fuerza + " de fuerza y "
                        + this.xp + " puntos de experiencia";
        return descripcion;
    },
    
};

var perso1 = Object.create(Personaje);
perso1.nombre = "Héroe";
perso1.vida = 150;
perso1.fuerza = 25;

var perso2 = Object.create(Personaje);
perso2.nombre = "Mago";
perso2.vida = 130;
perso2.fuerza = 35;

console.log(perso1.describir());
console.log(perso2.describir());

 

 

Resultado de la ejecución
Resultado de la ejecución

 

En el ejemplo anterior, hemos creado un objeto  Personaje que contiene características comunes a todos los personajes del juego. Los objetos  perso1  y  perso2 se crean teniendo el objeto  Personaje  como prototipo y le delegan las funcionalidades comunes.

 

Inicialización de personajes

Puede que notes que este proceso de creación de un personaje es un poco largo. Hay que añadir una linea de código por cada nueva propiedad que quieras añadir al personaje. Podríamos añadir una función de inicialización del personaje, en el que le pasamos todas las propiedades que queremos que se asignen al objeto.

 

var Personaje = {

    // Inicializa el personaje
    init: function (nombre, vida, fuerza) {
        this.nombre = nombre;
        this.vida = vida;
        this.fuerza = fuerza;
        this.xp = 0;
    },

    // Retorna la descripción del personaje
    describir: function () {
        var descripcion = this.nombre + " tiene " 
                        + this.vida + " puntos de vida y " 
                        + this.fuerza + " de fuerza y "
                        + this.xp + " puntos de experiencia";
        return descripcion;
    },
    
};

var perso1 = Object.create(Personaje);
perso1.init("Héroe", 150, 25);

var perso2 = Object.create(Personaje);
perso2.init("Mago", 130, 35);

console.log(perso1.describir());
console.log(perso2.describir());

 

El método  init() toma como parámetros  los valores con los que queremos iniciar las propiedades de un personaje. En el interior de la función, hay que distinguir las propiedades (a las que se hace referencia con la palabra clave  this ) de los parámetros (los valores que pasamos al método, que no van junto con this ). Por ejemplo,  this.nombre representa la propiedad  nombre  del objeto y el  nombre  (sin  this ) corresponde a uno de los parámetros que pasamos a la función.

 El código de creación de un personaje se divide en dos etapas:

  • La creación del objeto a partir del modelo o prototipo  Personaje .

  • La inicialización de las propiedades con ayuda de una función  init() del objeto Personaje.

 

Añadiendo enemigos

A pesar de disponer de un segundo personaje, nuestro juego necesita un enemigo contra el que luchar.

Al igual que los personajes, el enemigo tendrá un nombre, puntos de vida y fuerza. La diferencia será que el enemigo no ganará puntos de experiencia, pero guardará la cantidad de puntos que ganará el personaje que le mate.

Tantos los jugadores como los enemigos son dos personajes, con puntos en común y puntos que les distinguen. Nuestro objeto prototipo deberá reflejar estas diferencias.

var Personaje = {
    // Inicia el personaje
    initPersonaje: function (nombre, vida, fuerza) {
        this.nombre = nombre;
        this.vida = vida;
        this.fuerza = fuerza;
    }
};

var Jugador = Object.create(Personaje);
// Metodo que inicia el jugador
Jugador.initJugador = function (nombre, vida, fuerza) {
    // obtiene las propiedades de Personaje, comunes tanto a jugadores como enemigo definidas en Personaje
    this.initPersonaje(nombre, vida, fuerza);
    this.xp = 0; // Se inicia la experiencia de Jugador en 0
};
// Metodo propio de Jugador que retorna la descripción del jugador
Jugador.describir = function () {
    var descripcion = this.nombre + " tiene "
                    + this.vida + " puntos de vida, "
                    + this.fuerza + " de fuerza y "
                    + this.xp + " puntos de experiencia";
    return descripcion;
};

var Enemigo = Object.create(Personaje);
// Metodo que inicia el enemigo
Enemigo.initEnemigo = function (nombre, vida, fuerza, raza, cantidadXp) {
    // obtiene las propiedades de Personaje, comunes tanto a jugadores como enemigo definidas en Personaje
    this.initPersonaje(nombre, vida, fuerza);
    // añadimos dos propiedades comunes sólo a los enemigos
    this.raza = raza;
    this.cantidadXp = cantidadXp;
};

var jugador1 = Object.create(Jugador);
jugador1.initJugador("Héroe", 150, 25);

var jugador2 = Object.create(Jugador);
jugador2.initJugador("Mago", 130, 35);

var monstruo = Object.create(Enemigo);
monstruo.initEnemigo("ZogZog", 40, 20, "orco", 10);

console.log(jugador1.describir());
console.log(monstruo.nombre);

Este paso es un poco más complejo, pero vamos a ver punto por punto qué estamos haciendo.

Recuerda que partimos de la base de que no queremos repetir código. Por lo tanto, todas las características que los jugadores y los enemigos comparten, queremos escribirlas una sola vez.

Creamos el objeto  Personaje que contendrá las características compartidas tanto por los jugadores como por lo enemigos. Añadimos un método  initPersonaje() , que permite asignar los valores de las propiedades comunes (nombre, vida y fuerza). Queremos que este método esté presente en ambos tipos de personaje para poder hacer uso de él, asignando diferentes valores. 

Para contar con el método initPersonaje(), tanto el objeto  Jugador  como  Enemigo serán objetos creados a partir del prototipo  Personaje :

// Creamos un nuevo objeto Jugador, a partir del prototipo Personaje
// Esto quiere decir que todas las propiedades y métodos del objeto
// Personaje, estarán disponibles como métodos del objeto Jugador y Enemigo
var Jugador = Object.create(Personaje);

// ...

var Enemigo = Object.create(Personaje);

Una vez el objeto Jugador ha sido creado, le añadimos dos métodos específicos que sólo los jugadores tendrán:  describir()  y  initJugador() . Hacemos lo mismo para los enemigos, añadiendo el métodoinitEnemigo() .

Una vez hecho esto, podemos crear dos nuevos objetos,jugador1ymonstruo que tengan como prototipo aJugadoryEnemigo. Tanto Jugador como Enemigo, comparten el prototipoPersonaje.

 

 

¡Que comience el combate! 

Hasta ahora nuestros personajes no cuentan con ningún método que les permita atacarse. Si no hubiera pelea el juego sería un rollo, así que vamos añadir el código necesario para que puedan darse un poco de leña.

Un jugador puede atacar a un enemigo, y viceversa. El personaje atacado ve reducidos sus puntos de vida en una cantidad igual a los puntos de fuerza del atacante. Si el número de puntos de vida llega a 0, el personaje muere. Si un jugador gana el combate, recibe un número de puntos iguales a la cantidad de punto de experiencia del enemigo.

Podemos considerar que atacar es una capacidad común de jugadores y enemigos, con la particularidad que la suma de experiencia es específica de los jugadores. Analiza la siguiente modelización.

 

var Personaje = {
    // Inicia el personaje
    initPersonaje: function (nombre, vida, fuerza) {
        this.nombre = nombre;
        this.vida = vida;
        this.fuerza = fuerza;
    },
    
    // Ataca a un personaje objetivo
    atacar: function (objetivo) {
        if (this.vida > 0) {
            var golpe = this.fuerza;
            console.log(this.nombre + " ataca a " 
                        + objetivo.nombre + " produciéndole un daño de " 
                        + golpe + " puntos");
            objetivo.vida = objetivo.vida - golpe;
            if (objetivo.vida > 0) {
                console.log(objetivo.nombre + " tiene todavía " 
                            + objetivo.vida + " puntos de vida");
            } else {
                objetivo.vida = 0;
                console.log(objetivo.nombre + " ha muerto");
            }
        } else {
            console.log(this.nombre + " no puede atacar: está muerto...");
        }
    }
};


var Jugador = Object.create(Personaje);
// Inicia el personaje
Jugador.initJugador = function (nombre, vida, fuerza) {
    this.initPersonaje(nombre, vida, fuerza);
    this.xp = 0;
};
// Retorna la descripción del jugador
Jugador.describir = function () {
    var descripcion = this.nombre + " tiene " 
                    + this.vida + " puntos de vida, " 
                    + this.fuerza + " de fuerza y " 
                    + this.xp + " puntos de experiencia";
    return description;
};
// Combate a un adversario
Jugador.combatir = function (adversario) {
    this.atacar(adversario);
    if (adversario.vida === 0) {
        console.log(this.nombre + " ha matado " 
                    + adversario.nombre + " y gana " 
                    + adversario.cantidadXp + " puntos de experiencia");
        this.xp += adversario.cantidadXp;
    }
};

var Enemigo = Object.create(Personaje);
// Metodo que inicia el enemigo
Enemigo.initEnemigo = function (nombre, vida, fuerza, raza, cantidadXp) {
    this.initPersonaje(nombre, vida, fuerza);
    this.raza = raza;
    this.cantidadXp = cantidadXp;
};

// ...

El objeto Personaje posee un nuevo métodoatacar()  que se encarga de atacar aun objetivo, así como lo casos particulares asociados: muerte del objetivo o ataque a un objetivo ya muerto. El objeto Jugador tiene un métodocombatir() que llama al métodoatacar() de Personaje y se encarga de gestionar la obtención de experiencia en caso de muerte del objetivo. El objetoEnemigo  no ha sido modificado, pero puede atacar ya que el métodoatacar()  de Personaje se encuentra en su cadena de prototipos.

Ya podemos crear personajes y enemigos y comenzar a combatir.

// ...

var jugador1 = Object.create(Jugador);
jugador1.initJugador("Héroe", 150, 25);

var jugador2 = Object.create(Jugador);
jugador2.initJugador("Mago", 130, 35);


console.log(jugador1.describir());
console.log("----+++++----");
console.log(jugador2.describir());
console.log("----+++++----");

var monstruo = Object.create(Enemigo);
monstruo.initEnemigo("ZogZog", 40, 20, "orco", 10);

console.log(monstruo.nombre + ", un " + monstruo.raza + ", aparece de repente ");

monstruo.atacar(jugador1);
monstruo.atacar(jugador2);

jugador1.combatir(monstruo);
jugador2.combatir(monstruo);

console.log(jugador1.describir());
console.log(jugador2.describir());

ZogZog ataca a los dos personajes, que responden también atacando al orco. La ejecución del programa muestra el siguiente resultado. 

Pelea entre dos jugador y un enemigo
Pelea entre dos jugador y un enemigo

 

Analiza los métodos atacar()ycombatir()  y asegúrate de entenderlos bien. Prueba cambiando valores de fuera y vida para obtener diferentes resultados.

 

Conclusión

Ahora conoces los grandes principios de la programación orientada a objetos en JavaScript. La POO permite reunir los datos y los comportamientos (los métodos) en entidades llamadas objetos.  

Como ya sabes, el modelo de objetos en JavaScript se basa en prototipos, a partir de los cuales crear nuevos objetos y compartir propiedades entre ellos. Cada objeto tiene un prototipo, si intentas acceder a una propiedad o método de ese objeto, JavaScript la buscará en primer lugar en el propio objeto y si no la encuentra continuará buscando en sus prototipos.

La POO se utiliza mucho hoy en día, sin embargo no es la única forma de crear programas eficaces. Es posible combinar la utilización de objetos con simples funciones en un mismo programa, o incluso no utilizar objetos. 

 

 Practica tú mismo

Para comprender algo nuevo, no hay nada mejor que realizar ejemplos prácticos que te ayuden a interiorizar los nuevos conceptos.

Modelización de un objeto perro

Crea un objeto Perro que sirva de prototipo para crear otros perros. Dispondrá de 3 propiedades: nombre, raza y talla. Además, tendrá un método ladrar() que retornará un sonido diferente en función de la talla del perro.

 

var Perro = {
    // Código del objeto Perro
}

var peluca = Object.create(Perro);
peluca.init("Peluca", "Pastor Aleman", 40);
console.log(peluca.nombre + " es un " + peluca.raza + " y mide " + peluca.talla + " cm");
console.log("¡Mira un gato! " + peluca.nombre + " ladra: " + peluca.ladrar());

var pitu = Object.create(Perro);
pitu.init("Pitu", "Salchicha", 19);
console.log(pitu.nombre + " es un " + pitu.raza + " y mide " + pitu.talla + " cm");
console.log("¡Mira un gato! " + pitu.nombre + " ladra: " + pitu.ladrar())

 El resultado es el siguiente: 

Resultado de crear dos perrros
Resultado de crear dos perrros

 

Solución del ejercicio

Intenta primero resolver el ejercicio propuesto por ti mismo. Si ves que te bloqueas, suele venir bien cambiar de actividad y volver a intentarlo más tarde. Si no lo consigues, puedes consultar las soluciones aquí

 

 

 

 

 

 

Example of certificate of achievement
Example of certificate of achievement