En los resúmenes de los distintos paradigmas de programación se suele mencionar la programación funcional, lógica, procedural, orientada a objetos…

Dentro de la programación orientada a objetos, hay distintos tipos. El más conocido es el basado en clases, donde encontramos lenguajes como Java, C++, C#… Sin embargo, hay más tipos de programación orientada a objetos, y es usada por lenguajes muy conocidos. Me estoy refiriendo a la programación basada en prototipos, y su lenguaje más conocido es JavaScript.

Según la Wikipedia: Programación basada en prototipos es un estilo de programación orientada a objetos en el cual, las “clases” no están presentes, y la re-utilización de procesos (conocida como herencia en lenguajes basados en clases) se obtiene a través de la clonación de objetos ya existentes, que sirven de prototipos, extendiendo sus funcionalidades.

Es decir, se trata de un paradigma orientado a objetos sin clases. ¿Cómo es esto? En lugar de definir una clase, para después crear una instancia de ella, lo que hacemos es definir directamente el nuevo objeto, con sus métodos y atributos. En la página web de Mozilla tenemos un buen resumen de las diferencias existentes entre la POO basada en prototipos y la basada en clases.

Una guía muy buena para entender, paso a paso, en qué consiste, y cómo funciona, es la que proporciona de nuevo Mozilla en su artículo “A re-introduction to JavaScript“, en el apartado “Custom Objects“. Al no estar en español, voy a copiarlos aquí:

Consideremos un objeto persona con los campos nombre y apellido. Hay dos formas de mostrar: “nombre apellido” o “apellido, nombre”. Esta es una forma de hacerlo:

function makePerson(first, last) {
    return {
        first: first,
        last: last
    }
}
function personFullName(person) {
    return person.first + ' ' + person.last;
}
function personFullNameReversed(person) {
    return person.last + ', ' + person.first
}

Esto funciona, pero es bastante feo. Terminas con docenas de funciones y tu espacio de nombres global. Lo que realmente necesitamos es una forma de vincular una función a un objeto. Puesto que las funciones son objetos, esto es fácil:

function makePerson(first, last) {
    return {
        first: first,
        last: last,
        fullName: function() {
            return this.first + ' ' + this.last;
        },
        fullNameReversed: function() {
            return this.last + ', ' + this.first;
        }
    }
}

Podemos aprovechar la palabra clave ‘this‘ para mejorar nuestra función makePerson:

function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = function() {
        return this.first + ' ' + this.last;
    }
    this.fullNameReversed = function() {
        return this.last + ', ' + this.first;
    }
}

Nuestro objeto persona ha mejorado, pero aun tiene algunos flecos feos. Cada vez que creamos un objeto persona, estamos creando dos veces las funciones que hay en cada uno – ¿no sería genial si pudieran compartir este código?

function personFullName() {
    return this.first + ' ' + this.last;
}
function personFullNameReversed() {
    return this.last + ', ' + this.first;
}
function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = personFullName;
    this.fullNameReversed = personFullNameReversed;
}

Esto está mejor: estamos creando las funciones sólo una vez, y asignando referencias a ellas dentro del constructor. ¿Podemos hacerlo todavía mejor? La respuesta es sí:

function Person(first, last) {
    this.first = first;
    this.last = last;
}
Person.prototype.fullName = function() {
    return this.first + ' ' + this.last;
}
Person.prototype.fullNameReversed = function() {
    return this.last + ', ' + this.first;
}

Person.prototype es un objeto compartido por todas las instancias de Person. Forma parte de una cadena de búsqueda (que tiene un nombre especial, “prototype chain”): cada vez que intentas acceder a una propiedad de Person que no está asignada, JavaScript comprobará en Person.prototype para ver si esta propiedad existe ahí. Como resultado, cualquier cosa que asignemos a Person.prototype estará disponible en todas las instancias de ese constructor mediante el objeto this.

Ahora, si os pasaba como a mí, ya tendréis claro qué es eso que hace tan “rarito” a JavaScript. Y se trata ni más ni menos de su forma de ser orientado a objetos.