Comunidad de diseño web y desarrollo en internet

Programación orientada a objetos con ECMAScript 6: Clases


Hasta ahora en JavaScript si uno quería programar orientado a objeto lo que tenía que hacer es aprovecharse de la herencia de prototipos que posee JS para poder crear las "clases", esto tenía siempre una sintaxis similar a esta:

Código :

function Persona(nombre, edad) {
  this.nombre = nombre;
  this.edad   = edad;
}
Persona.prototype.presentarse = function() {
  return 'Hola me llamo ' + this.nombre + ' y tengo ' + this.edad + ' años';
}

var Sergio = new Persona('Sergio', 22);
Sergio.presentarse(); // 'Hola me llamo Sergio y tengo 22 años'

Una pequeña variación podía ser definir la función presentarse dentro de Persona usando:

Código :

function Persona(nombre, edad) {
  this.nombre = nombre;
  this.edad   = edad;

  this.presentarse = function () {
    return 'Hola me llamo ' + this.nombre + ' y tengo ' + this.edad + ' años';
  }
}

Esta fue siempre la forma de hacer POO en JS y aunque los prototipos de JS son muy poderoso para los programadores que vienen de lenguajes orientados a objetos como Java o PHP les resulta extraña ya que no son propiamente objetos, o al menos no como el resto de lenguajes considera los objetos.


Nota: El soporte para clases de ECMAScript 6 en los navegadores actualmente es nulo, esto está todavía en desarrollo y no está listo para ser usado, hay sin embargo formas de escribir código usando ES6 y luego convertir el código a ES5 quedando un resultado bastante bueno, pero no es un soporte nativo, sigue siendo ES5.


Nueva sintaxis


Para facilitar esto en ECMAScript 6 se va a agregar una forma de crear estas clases, como en los demás lenguajes, aunque al final esta nueva sintaxis para hacer POO es simplemente una capa sobre la sintaxis actual mediante prototipos, solo que más simple de entender.

En la nueva sintaxis, si quisieramos hacer el mismo ejemplo de antes haríamos:

Código :

class Persona {
  // constructor donde definir las variables que se reciben y guardarlas en el objeto usando this
  constructor(nombre, edad) {
    this.nombre = nombre;
    this.edad   = edad;
  }

  // método para presentarse
  presentarse() {
    return 'Hola me llamo ' + this.nombre + ' y tengo ' + this.edad + ' años';
  }
}

var Sergio = new Persona('Sergio', 22);
Sergio.presentarse();

Cómo se puede ver, la sintaxis es mucho más similar a la de otros lenguajes (teniendo un constructor y métodos). Esto sin embargo es, como dije antes, una capa por encima del método actual, por esa razón al momento de instancia la clase el resto de la sintaxis es igual.

Extender clases


Al igual que en otros lenguajes de programación, una clase puede extender otra clase heredando métodos o propiedades de la clase padre. Siguiendo el ejemplo anterior, vamos a extender la clase Persona:

Código :

class Desarrollador extends Persona {
  constructor(nombre, edad, cargo) {
    super(nombre, edad);
    this.cargo = cargo;
  }

  presentarse() {
    return super.presentarse() + ' y soy desarrollador ' + this.cargo;
  }
}

var Sergio = new Desarrollador('Sergio', 22, 'Frontend');
Sergio.presentarse(); // 'Hola me llamo Sergio y tengo 22 años y soy desarrollador Frontend'

Como se puede ver la sintaxis es: indicar el nombre de la nueva clase y que extiende a otra clase. Luego si se desea extender algún método ya existente como el constructor se reciben los mismo parámetros que en la clase padre más los nuevos y luego se usa la función super().

La función super() ejecuta el método con el mismo nombre desde el que se está llando a super(), de esta forma al definir el nuevo constructor llamamos a super() y le pasamos los mismos parámetros que recibe el constructor de Persona, entonces se ejecuta ese constructor y luego código del nuevo.

En el caso del método presentarse use super.presentarse(), esto lo que hace es ejecutar el método presentarse de la clase padre, en este caso sería lo mismo que hacer simplemente super(), pero si hubiese otro método con otro nombre que queremos ejecutar usar super.metodo() nos permite llamar a un método de la clase padre desde cualquier otro método de la nueva clase.

Getters y Setters


En algunos lenguajes de programación (como Java) existen los getters y setters, que son lo que se llama mutator method, estos métodos que se usan para controlar variables internas de un objeto (propiedades). Para usarlos simplemente se agrega get o set delante del nombre del método de la siguiente forma:

Código :

class Persona {
  constructor(nombre, edad) {
    this.nombre = nombre;
    this.edad   = edad;
  }

  get verNombre() {
    return this.nombre;
  }
  set nuevoNombre(nuevo) {
    this.nombre = nuevo;
  }
}
var Sergio = new Persona('Sergio', 22);
Sergio.verNombre; // devuelve Sergio
Sergio.nuevoNombre('Daniel'); // cambia el valor de nombre a Daniel
Sergio.verNombre; // devuelve Daniel

Como se puede ver es bastante simple, definir un método get con el nombre que quieras (no puede ser el nombre de la propiedad) y este debería devolver el valor deseado (tecnicamente puede hacer cualquier cosa el método), ó definis un método set con otro nombre (tampoco el mismo de la propiedad) y que recibe el nuevo valor y lo asigna a this (también puede hacer cualquier cosa en realidad, esto es útil para poder validar el nuevo valor).

Aunque esto hace bastante más legible y limpio el código al tener métodos específicos para obtener o modificar propiedades del objeto la verdad es que no son necesarios ya que simplemente usando la sintaxis de objetos de toda la vida podes obtener el valor de una propiedad y modificarlo.

Código :

Sergio.nombre = 'Sergio'; // cambia el valor de nombre a 'Sergio'
Sergio.nombre; // Devuelve 'Sergio'


Características


Los nombres de las clase no pueden ser *eval* ó *arguments*; no están permitidos nombres de clase repetidos, el nombre *constructor* solo puede ser usado para métodos, no para getters, setter o un generador de métodos (otro características de ES6, quizá para otro tutorial).


Las clases no se pueden llamar antes de definirse.

Código :

new Persona(); // runtime error
class Persona {};

Aunque esto no debería ser un problema, todavía se puede instanciar la clase desde cualquier parte, solo es necesario esperar a que esté definida.


Si no hay método constructor, entonces el método por defecto va a ser:

Código :

constructor(arg1, arg2...) {
  super(arg1, arg2...);
}


Conclusión


Aunque la forma actual de realizar POO usando prototype es muy poderosa esta nueva forma va a traer varios beneficios ya sea que termine siendo más fácil para los principiantes, hacer que crear una subclase sea más fácil tanto para principantes como para expertos o hacer código más portable entre frameworks facilitando la reutilizaciónd e código.

De todas formas, al final sigue siendo la clásica forma de crear objetos debajo de toda la nueva sintaxis.

Referencias (en inglés)


¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.

Publica tu comentario

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate