Este es un repaso de lo que cubrimos en esta lección sobre diseño orientado a objetos.
Cuando creamos programas, a menudo encontramos que queremos crear muchos objetos diferentes que comparten propiedades parecidas, como muchos gatos, que tienen el pelo de color y tamaño ligeramente diferente, o muchos botones, con etiquetas y posiciones diferentes. Queremos ser capaces de decir "así es como se ve un gato en general" y luego decir "hagamos a este gato en específco, y luego a este otro gato, y serán parecidos en algunos aspectos pero también diferentes en otros". En ese caso, queremos usar diseño orientado a objetos para definir los tipos de objeto y crear nuevas instancias de esos objetos.
Para definir un tipo de objeto en JavaScript, primero tenemos que definir una función "constructora". Esta es la función que usaremos cada vez que queramos crear una nueva instancia de ese tipo de objeto. Aquí hay una función constructora para un tipo de objeto Book (libro):
var Book = function(title, author, numPages) {
  this.title = title;
  this.author = author;
  this.numPages = numPages;
  this.currentPage = 0;
};
La función toma argumentos para los aspectos que serán diferentes en cada libro: el título, autor y número de páginas. Después establece las propiedades iniciales del objeto con base en esos argumentos, al usar la palabra clave this. Cuando usamos this en un objeto, nos estamos refiriendo a la instancia actual de un objeto, refiriéndose a sí mismo. Necesitamos almacenar las propiedades en this para asegurarnos de que las podamos recordar más tarde.
Para crear una instancia de un objeto Book, declaramos una variable nueva para almacenarla, después utilizamos la palabra clave new (nuevo), seguida del nombre de la función constructora y pasamos los argumentos que espera el constructor:
var book = new Book("Sueños de Robot", "Isaac Asimov", 320);
Entonces podemos acceder a las propiedades que almacenamos en el objeto utilizando la notación de punto:
println("Me encantó leer " + book.title); // Me encantó leer Sueños de Robot
println(book.author + " es mi autor favorito"); // "Isaac Asimov" es mi autor favorito
Vamos a contrastar esto por un minuto, y mostrar qué hubiera pasado si no hubiéramos configurado correctamente nuestra función constructora:
var Book = function(title, author, numPages) {
};
var book = new Book("Little Brother", "Cory Doctorow", 380);
println("Me encantó leer " + book.title); // Me encantó leer undefined
println(book.author + " es mi autor favorito"); // undefined es mi autor favorito
Si le pasamos los argumentos a la función constructora pero no los almacenamos explícitamente en this, ¡entonces no seremos capaces de accederlos después! El objeto se olvidará de ellos.
Cuando definimos los tipos de objeto, muchas veces queremos asociar tanto las propiedades como el comportamiento con ellos, como todos nuestros objetos de gato que deberían ser capaces de maullar() y comer(). Así que necesitamos poder adjuntar funciones a nuestras definiciones de tipo de objeto, y podemos hacer eso definiéndolas en lo que se llama el object prototype (prototipo del objeto):
Book.prototype.readItAll = function() {
  this.currentPage = this.numPages;
  println("¡Leíste " + this.numPages + " páginas!");
};
Es como si definiéramos una función de manera normal, excepto que la colgamos del prototipo de Book en lugar de definirla globalmente. Así es como JavaScript sabe que esta es una función que se puede llamar en cualquier objeto Book, y que esta función debe tener acceso a this del libro sobre el cual se llama.
Entonces podemos llamar a la función (a la que llamamos un método, ya que está adjunta a un objeto), así:
var book = new Book("Rebelión en la granja", "George Orwell", 112);
book.readItAll(); // ¡Leíste 112 páginas!
Recuerda, el objetivo del diseño orientado a objetos es que nos facilita hacer múltiples objetos relacionados (instancias de objetos). Veamos eso en código:
var pirate = new Book("Pirate Cinema", "Cory Doctorow", 384);
var giver = new Book("El Dador", "Lois Lowry", 179);
var tuck = new Book("Tuck Everlasting", "Natalie Babbit", 144);

pirate.readItAll(); // ¡Leíste 384 páginas!
giver.readItAll(); // ¡Leíste 179 páginas!
tuck.readItAll(); // ¡Leíste 144 páginas!
Ese código nos da tres libros que son parecidos: todos tienen los mismos tipos de propiedades y comportamiento, pero también son diferentes. ¡Genial!
Ahora, si piensas acerca del mundo, los perros y los gatos son distintos tipos de objetos, así que probablemente crearías diferentes tipos de objeto para ellos si estuvieras programando un Gato y un Perro. Un gato podría maullar(), un perro podría ladrar(). Pero también son parecidos: tanto el perro como el gato podrían comer(), ambos tendrían una edad, un nacimiento y una muerte. Ambos son mamíferos y eso significa que tienen mucho en común, aunque también sean diferentes.
En ese caso, queremos usar la idea de la herencia del objeto. Un tipo de objeto podría heredar propiedades y comportamiento de un tipo de objeto padre, pero también tener sus propias cosas únicas. Todos los Gatos y Perros podrían heredar de Mamífero, así que no tendrían que inventarse el comer() desde cero. ¿Cómo haríamos eso en JavaScript?
Volvamos al ejemplo de libro y digamos que ese Book es el tipo de objeto "padre", y queremos hacer dos tipos de objetos que hereden de él: Paperback e EBook.
Un paperback es como un libro, pero tiene una cosa principal diferente, al menos para nuestro programa: tiene una imagen de portada. Entonces, nuestro constructor necesita tomar cuatro argumentos, para recibir esa información adicional:
var PaperBack = function(title, author, numPages, cover) {
  // ...
}
Ahora, no queremos tener que hacer todo el trabajo que ya hicimos en el constructor de Book para recordar esos primeros tres argumentos: queremos aprovechar el hecho de que el código para eso sería el mismo. Así que en realidad podemos llamar al constructor de Book desde el constructor de PaperBack y pasarle esos argumentos:
var PaperBack = function(title, author, numPages, cover) {
  Book.call(this, title, author, numPages);
  // ...
};
Todavía tenemos que almacenar la propiedad cover en el objeto, así que necesitamos una línea más que se encargue de eso:
var PaperBack = function(title, author, numPages, cover) {
  Book.call(this, title, author, numPages);
  this.cover = cover;
};
Ahora, tenemos un constructor para nuestro PaperBack, que le ayuda a compartir las mismas propiedades que los Books, pero también queremos que nuestro PaperBack herede sus métodos. Aquí está como hacer eso, diciéndole al programa que el prototipo de PaperBack debe estar basado en el prototipo de Book:
PaperBack.prototype = Object.create(Book.prototype);
También podríamos querer adjuntarle un comportamiento específico al paperback, como poder quemarlo, y podemos hacer eso al definir funciones en el prototipo, después de la línea de arriba:
PaperBack.prototype.burn = function() {
  println("Oh no, quemaste todas las " + this.numPages + " páginas");
  this.numPages = 0;
};
¡Y ahora podemos crear un nuevo paperback, leerlo todo y quemarlo!
var calvin = new PaperBack("The Essential Calvin & Hobbes", "Bill Watterson", 256, "http://ecx.images-amazon.com/images/I/61M41hxr0zL.jpg");

calvin.readItAll(); // ¡Leíste 256 páginas!
calvin.burn(); // Oh no, ¡quemaste todas las 256 páginas!
(Bueno, en realidad no vamos a quemarlo, porque es un libro asombroso, pero tal vez si estuviéramos varados en un desierto glacial y desesperados en busca de calor y a punto de morir).
Y ahora puedes ver cómo podemos utilizar los principios de diseño orientado a objetos en JavaScript para crear datos más complejos para tus programas y modelar mejor los mundos de tus programas.