Programación Orientada a Objetos | Guía práctica | Parte #3

Tercera y última parte de esta saga sobre Programación Orientada a Objetos (en inglés, Object Oriented Programming). Si acabas de aterrizar aquí, puedes ver las partes #1 y #2 antes. 

Funcionamiento de la herencia en un prototype model

Herencia de propiedades

Partimos del código tal y como lo dejamos en la parte #2:

function User(username, email) {
    this.username = username;
    this.email = email;
}

User.prototype.login = function() {
    console.log(`${this.username} is logged in`);
    return this;
}

User.prototype.logout = function() {
    console.log(`${this.username} is logged out`);
    return this;
}

const userOne = new User('gandalf', 'gandalf@thewhite.com');
const userTwo = new User('frodo', 'frodo@bolson.com');

console.log(userOne, userTwo);

userOne.login().logout();

Cuando construimos un objeto basado en otro "superior" con la sintaxis moderna de las clases, JS se encarga de la herencia por nosotros. Pero si queremos construir una subclase de la manera clásica, debemos encargarnos nosotros del tema. 

Vamos a intentar replicar la creación de la subclase Adminpero de la manera tradicional (sin usar la keyword "class").

1. Creamos la constructor function para construir el Admin object, que recibe los mismos parámetros que el User object.

2. Creamos una instance del Admin object, a la que llamamos userThree.

3. Para heredar las propiedades de un "objeto superior", del User en este caso, no vamos a usar la keyword "super", porque eso es parte de la sintaxis moderna. Así que tenemos que encontrar otra manera de llamar a la constructor function del User object. Ahí es donde entra en juego el método call

Si aplicamos el método call al User object, éste llamará al constructor del User.

Al método call le pasamos varios argumentos:

: la keyword "this", que en este caso hace referencia al Admin object.

y : las propiedades que queremos heredar del User object.

4. Hacemos un console.log del userThree, así podremos ver en la consola que el userThree es un Admin que ha heredado las propiedades del User object.

function Admin(username, email) {
    User.call(this, username, email);
}

const userOne = new User('gandalf', 'gandalf@thewhite.com');
const userTwo = new User('frodo', 'frodo@bolson.com');
const userThree = new Admin('sauron', 'sauron@mordor.com');


console.log(userOne, userTwo, userThree);

5. Añadimos propiedades exclusivas del Admin object, por ejemplo un title, como hacíamos antes con las clases. Le pasamos ese argumento a la instance del Admin (el userThree). 

function Admin(username, email, title) {
    User.call(this, username, email);
    this.title = title;
}

const userOne = new User('gandalf', 'gandalf@thewhite.com');
const userTwo = new User('frodo', 'frodo@bolson.com');
const userThree = new Admin('sauron', 'sauron@mordor.com', 'supreme master');

Y así hemos conseguido heredar propiedades de un objeto superior a un "objeto hijo". 

Herencia de métodos

Cuando construimos una clase de JS con la sintaxis moderna, los métodos son automáticamente añadidos al prototype de ese objeto. Sin embargo, con la forma más antigua de construir una clase (usando el prototype model), debemos hacerlo nosotros manualmente

Para demostrar esto, prueba a escribir Admin.prototype en la consola. Verás que los métodos del User (login logout) no aparecen. 

La manera de conseguir heredar los métodos del objeto superior al Admin es creando un objeto nuevo dentro del prototype del Admin y basar ese objeto en el User object. Lo hacemos usando el método createComo argumento le pasamos el User.prototype.

Admin.prototype = Object.create(User.prototype);

Si ahora escribes Admin.prototype en la consola, verás que dentro del __proto__ ya tenemos los métodos heredados del User object. ¡Estupendo! 

 Fíjate que, si expandes el Admin object, verás también la propiedad __proto__ apuntando al User object Ese es el indicador de que estamos heredando métodos de ese objeto. Pero verás que tienes que volver a expandir esa propiedad para poder ver los métodos. Es como si estuviese a dos escalones de distancia en lugar de a uno. 

|   Elements    Console    Sources    Performance    Network    ...

Admin
    email: "sauron@mordor.com"
    title: "supreme master"
    username: "sauron"
    __proto__: User
        __proto__:
        login: ƒ ()
        logout: ƒ ()
        constructor: ƒ User(username, email)
        __proto__: Object

Puedes hacer la prueba creando un método directamente sobre el prototype del Admin.

Admin.prototype.deleteUser = function() {
    // logic here
};

Si vas a tu consola y expandes el Admin object, verás que el método deleteUser está en el primer nivel del prototype, y no en el segundo, como el login y el logout. 

Si siguiésemos creando objetos basados en su objeto superior (por ejemplo, un SuperAdmin object que heredase las características del Admin object, la "escalera de métodos" añadiría un tercer escalón, y así sucesivamente. 

A esto se le llama encadenado de prototipos (en inglés, prototype chain), y es el esqueleto en el que se basan las modernas clases de JavaScript. 

La base de todos los objetos

Sabiendo todo lo anterior, vamos a indagar un poco más sobre esa "escalera de métodos" cuando un objeto hereda métodos del prototype de un objeto superior. Tomemos por ejemplo un objeto de tipo array. Creamos uno en la consola.

Si lo expandes, verás que en el "primer escalón" o nivel ya tiene su prototype visible, que apunta a los métodos de los arrays. 

Si sigues expandiéndolo, encontrarás otro prototype en el segundo nivel que apunta al prototype de un objeto.

|   Elements    Console    Sources    Performance    Network    ...

new Array(1, 3, 4, 5, 6)
(5) [1, 3, 4, 5, 6]
    0: 1
    1: 3
    2: 4
    3: 5
    4: 6
    length: 5
    __proto__: Array(0)
        concat: ƒ concat()
        más el resto de métodos de los arrays
        __proto__: Object

Es decir, que el array object siempre hereda métodos de un objeto superior. Esos métodos son los mismos que si creamos un objeto vacío y accedemos a su prototype. 

|   Elements    Console    Sources    Performance    Network    ...

new Object
    {}
    __proto__:
        constructor: ƒ Object()
        hasOwnProperty: ƒ hasOwnProperty()
        isPrototypeOf: ƒ isPrototypeOf()
        propertyIsEnumerable: ƒ propertyIsEnumerable()
        toLocaleString: ƒ toLocaleString()
        toString: ƒ toString()
        valueOf: ƒ valueOf()
        __defineGetter__: ƒ __defineGetter__()
        __defineSetter__: ƒ __defineSetter__()
        __lookupGetter__: ƒ __lookupGetter__()
        __lookupSetter__: ƒ __lookupSetter__()
        get __proto__: ƒ __proto__()
        set __proto__: ƒ __proto__()

 Sólo que en este objeto superior están en el primer nivel. 

Prueba a hacer otro ejemplo con otro tipo de objeto, como un XMLHttpRequest.

new XMLHttpRequest

Verás que tienes que desplegar tres escalones hasta llegar al prototype del Object, pero al final, se llega. 

Lo que esto nos enseña es que todo objeto en JavaScript hereda los métodos pertenecientes al prototype de Object. Es como un "tipo base" u "objeto padre" del resto de objetos.

THE END!

¡Y con esto terminamos nuestra serie sobre POO! Espero que hayas aprendido algo nuevo.  Si te queda alguna duda, ¡nos vemos en los comentarios!

Otros artículos que pueden interesarte

Cómo aprendí a programar cuando estaba «programada» para ser de letras
[tcb-script src="https://player.vimeo.com/api/player.js"][/tcb-script]A nadie le gusta su trabajo. Eso es lo que me decía a mí misma cuando conseguí mi primer[...]
Días del 160 al 203 – ¡Primer objetivo conseguido!
“A veces podemos pasarnos años sin vivir en absoluto, y de pronto toda nuestra vida se concentra en un solo[...]
Claves para entender Angular. Qué es y cómo se utiliza
Angular es un framework creado por Google que nos permite construir Single Page Applications (SPA, por sus siglas en inglés).Frameworks¿Pero qué es[...]
Si crees que este post puede serle útil a alguien, por favor, ¡compártelo!:

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Esta web utiliza cookies para asegurar que se da la mejor experiencia al usuario. Si continúas utilizando este sitio se asume que estás de acuerdo. más información

Los ajustes de cookies en esta web están configurados para «permitir las cookies» y ofrecerte la mejor experiencia de navegación posible. Si sigues usando esta web sin cambiar tus ajustes de cookies o haces clic en «Aceptar», estarás dando tu consentimiento a esto.

Cerrar