Prototipos
Pero claro,
con esto solo definimos un único objeto, con sus atributos y métodos. Pero,
¿qué pasa si queremos crear una “clase” de la que poder instanciar objetos?
Como hemos dicho antes, en Javascript no hay clases, sino prototipos: objetos
de los que se copian otros objetos. Así que lo que haremos es crear un objeto
del que poder copiar sus propiedades. Para esto Javascript tiene un gran truco:
hace que cualquier función pueda ser instanciada como un nuevo objeto usando la
palabra clave “new”.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A simple vista
no es más que una función, ¡y es verdad que lo es! pues podemos llamar a
Saludator() como una función de toda la vida. Pero eso no quita que sea también
una factoría y que con ella podamos crear nuevos objetos:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Pero claro,
con este ejemplo tan sumamente sencillo no se aprecia que hemos creado un nuevo
objeto. Así que vamos a añadirle un par de propiedades y métodos (recordemos
que solo tenemos que declararlas con el prefijo this).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
En este ejemplo ya vemos cosas
nuevas:
– Que la función Saludator() es el constructor del objeto, y podemos pasarle
parámetros que posteriormente podemos usar en los atributos del objeto.
– Que dentro de la función saluda sigue siendo necesario usar this.nombre para
acceder al atributo nombre.
Por cierto, podemos crear
nuestros prototipos usando funciones como expresiones, y no como declaraciones.
El siguiente código es completamente equivalente al anterior:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Es necesario
señalar que las funciones que servirán para crear objetos se suelen nombrar con
la primera letra en mayúscula. Esto es así para evitar que se confundan con
funciones normales o variables.
Herencia
Como hemos
dicho antes, la herencia basada en clases no existe en Javascript, sino que es
herencia basada en prototipos. Esto significa que no hay una jerarquía de
clases real (con su extends, acceso super, etc.), sino que cada “clase” tiene
un objeto de referencia (como si fuera su clase padre) donde consultar
atributos y métodos en caso de que nuestra clase no los encuentre. Podemos
intentar simular una jerarquía (ojo, simular) usando estos objetos de
referencia, llamados prototipos, con una propiedad especial que tienen todas
las “clases” llamada prototype. Esta propiedad es asignable y su labor consiste
en guardar un objeto. Veámoslo con un ejemplo:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Con esto lo
que hacemos es asignar a la variable estática prototype en la clase Saludator
un simple objeto con un atributo apellido y un método despide. Cuando invocamos
a obj1.despide(), Javascript primero consulta en la propia clase Saludator y
busca ese método; al no encontrarlo, consulta en el objeto prototype, donde si
lo encuentra, por lo que lo ejecuta. Dentro de este método, se accede a
this.nombre y this.apellido y sucede lo mismo: primero Javascript mira en la
clase Saludator, donde solo encuentra el atributo nombre. Sin embargo, para
encontrar el atributo apellido debe consultar en el objeto prototype.
Todas las
clases tienen por defecto un objeto prototype vacío, como si al crearlas se
hiciera prototype = {}. Por esta razón, podemos acceder a esta variable y
asignarle los atributos de uno en uno. El siguiente ejemplo es completamente
equivalente al anterior:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
El problema de
la herencia basada en prototipos es que la “clase padre” no es una clase, sino
un objeto que se crea una única vez y que sirve de referencia para todas las
instancias de la clase. Además, en una herencia basada en clases como la de
Java, los constructores de las clases padre se heredan automáticamente (aunque
no se invocan al instanciar nuevos objetos), pero en una de prototipos no. Así
que para poder heredar e invocar constructores en Javascript tenemos que
hacerlo a mano. Primero debemos crear una referencia al constructor de la clase
padre y después llamarlo explícitamente:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Finalmente
decir que podemos acceder al atributo prototype en todas clases, incluso en las
clases del sistema, como String o Date. El siguiente código añade un nuevo
método capitalize() a la clase de sistema String:
|
|
|
|
|
|
|
|
|
|
|
|
DEMOSTRACIÓN
En el espacio de nombre del proyecto creado anteriormente damos click derecho y seleccionamos Add à New Items
En la siguiente ventana seleccionamos la Opción “Pagina HTML” y colocamos como nombre del archivo “Encapsulacion.html”, para luego presionar el botón Add.
Empezamos a declarar nuestro Objeto llamado “Persona”
Al ejecutar en nuestro navegador:
Sobrescribimos la función usando los prototipos
Y el resultado es el siguiente
Ahora crearemos una clase de nombre “Estudiante” que herede de “Persona” a través de los prototipos, el código completo quedaría de la siguiente manera:
Y terminamos nuestro ejemplo viendo los resultados en la consola del navegador