Clases de objetos en ES6

facebook-svg gplus-svg twitter-svg

Los objetos en JavaScript, al igual que en muchos otros lenguajes de programación, son una manera eficiente de organizar los datos. Desde hace poco, en ES6 tenemos la posibilidad de utilizar clases para crear objetos. Para entender cómo hacerlo veamos un ejemplo:

Este ejemplo crea la clase Pelota que dibuja una pelota en el <canvas>. Para empezar, en el HTML tenemos un elemento <canvas>. En el javascript primero hay que iniciar el canvas :


  // identifica el canvas
  const canvas = document.querySelector("canvas");
  // recupera el contexto del canvas
  const ctx = canvas.getContext("2d");
  // establece la anchura (cw) y la altura (ch) del canvas
  const cw = canvas.width = 300;
  const ch = canvas.height = 300;

Lea más acerca de cómo trabajar en canvas: Canvas una introducción.php

En ES6 creamos una nueva clase de esta manera:

class Pelota {
  . . . .
}

El único método imprescindible de una clase es el método constructor.

class Pelota {
  constructor(pos) {
      this.pos = pos;
      this.radio = 25;
      this.color = "tomato";
  } 
  . . . . . . 
}

Ahora podemos instanciar un nuevo objeto pelota de esta manera:

const pelota = new Pelota(pos);

Donde pos es la posición de la nueva pelota, el mismo argumento que toma el constructor.

También podemos crear otros métodos, como por ejemplo un método para dibujar la pelota:


class Pelota {
  constructor(pos) {
      this.pos = pos;
      this.radio = 25;
      this.color = "tomato";
  } 
  dibujar() {
      ctx.beginPath();
      ctx.arc(this.pos.x, this.pos.y, this.radio, 0, 2 * Math.PI);
      ctx.fillStyle = this.color;
      ctx.fill();
    }
  }

Si necesitamos añadir otro método una vez creada la nueva clase, tenemos que utilizar la palabra clave prototype

class Pelota {
      . . . . . .
}

Pelota.prototype.dibujar = function() {
      ctx.beginPath();
      ctx.arc(this.pos.x, this.pos.y, this.radio, 0, 2 * Math.PI);
      ctx.fillStyle = this.color;
      ctx.fill();
    }
}

Lea más acerca de cómo dibujar arcos y círculos en <canvas>

See the Pen ES6 clases #0* by Gabi (@enxaneta) on CodePen.

Métodos estáticos

Los métodos estáticos están asociados a la clase y no al objeto.   Hemos visto que para dibujar una pelota primero tengo que instanciar un nuevo objeto. Después puedo llamar el método dibujar del nuevo objeto pelota.

const pelota = new Pelota(pos);
pelota.dibujar();

Para llamar un método estático no necesito instanciar un nuevo objeto, ya que se trata de un método asociado a la clase Pelota, y no a los objetos pelota.

Pelota.metodoEstatico();

Para crear un nuevo método estático utilizamos la palabra clave static. En este caso voy a crear un método estático que genera una nueva pelota y la dibuja:

class Pelota {
  . . . . . . 
  static nuevaPelota() {
      const pos = {};
      pos.x = ~~(Math.random() * cw);
      pos.y = ~~(Math.random() * ch);
      const p = new Pelota(pos);
      p.dibujar();
    }
}

Si necesitamos añadir otro método estático una vez creada la nueva clase,

class Pelota {
  . . . . . . 
}
    
Pelota.nuevaPelota = function() {
    const pos = {};
    pos.x = ~~(Math.random() * cw);
    pos.y = ~~(Math.random() * ch);
    const p = new Pelota(pos);
    p.dibujar();
}

Ahora puedo crear y dibujar una nueva pelota de esta manera:

Pelota.nuevaPelota();

See the Pen ES6 objetos: clases #1 by Gabi (@enxaneta) on CodePen.

Setters y getters

Los setters tienen que tener exactamente un parámetro, y se utilizan para establecer el valor de una propiedad. Los setters no son funciones sino que enlazan la propiedad de un objeto con una función que será llamada para asignarle un nuevo valor. Por ejemplo:

set hsl(h) {
      this.color = `hsl(${h},100%,50%)`;
}

En este caso h ( hue ) es el parámetro que necesitamos para establecer un nuevo valor de this.color.
Para utilizar el setter hsl escribimos:

pelota.hsl = 45;

En este caso el nuevo color de la pelota es hsl(45, 100%,50%).  Sin embargo console.log (pelota.hsl) devuelve undefined porque todavía no tenemos un geter:

Un getter enlaza la propiedad de un objeto con una función que puede ser llamada cuando la propiedad es buscada.

get hsl(){
      return this.color
  }

Ahora console.log (pelota.hsl) devuelve el nuevo color de la pelota ( pelota.color )

See the Pen ES6 objetos: clases #1 by Gabi (@enxaneta) on CodePen.

Extender una clase en ES6

La palabra clave extends se utiliza para crear una nueva clase que extiende otra clase existente. El mejor ejemplo en que puedo pensar es dibujar trazados en SVG.

Para aprender SVG puede comenzar aquí: SVG - una introducción

Podemos crear una clase elementoSVG y la podemos extender para crear todo tipo de polígonos regulares y estrellas y toda clase de trazados.
Vamos por pasos:

Crear la clase Circulo

Para crear un elemento SVG que dibuja un circulo tenemos que escribir algo así:

<circle cx ="50" cy ="75" r ="40" />

donde cx y cy representan las coordenadas del centro y r es el radio del circulo. También podemos definir otros atributos como por ejemplo: stroke ( el color del borde ), stroke-width ( la anchura del borde ) o fill ( el color de relleno ).

Lea más acerca de Formas geométricas en SVG: circulo

Para crear el mismo circulo utilizando JavaScript podemos escribir:


// crea un nuevo elemento circle
const circ = document.createElementNS("http://www.w3.org/2000/svg", "circle");
// establece el valor de cada atributo
circ.setAttributeNS(null, "cx", 150);
circ.setAttributeNS(null, "cy", 150);
circ.setAttributeNS(null, "r", 25);
circ.setAttributeNS(null, "id", "circulo1");
// agrega el circulo al elemento padre (SVG en este caso)
SVG.appendChild(circle);

Lea más acerca de los espacios de nombre en SVG

La verdad es que hay muchísimo código que se repite. Lo podemos hacer mejor. Podemos crear un objeto que contiene todos los atributos del circulo que queremos crear:


let atributos = {
    cx:150,
    cy:150,
    r:25,
    id:"circulo1"
  }

Después podemos crear una clase que construye círculos en base a estos atributos:


class Circulo {
    // el método constructor 
    constructor(atributos) {
      this.atributos = atributos;
  }
  dibujar() {
      // crea un nuevo elemento "circle" 
      const circ = document.createElementNS(SVG_NS, "circle");
      // para cada atributo en el objeto atributos
      for (var atributo in this.atributos) {
        if (this.atributos.hasOwnProperty(atributo)) {
          // establece el valor del mismo atributo para el circulo
          circ.setAttributeNS(null, atributo, this.atributos[atributo]);
        }
      }
      // agrega el elemento "circle" al SVG 
      SVG.appendChild(circ);
      return circ;
    }
}
Crear la clase elementoSVG

De hecho, haciendo unos cuantos pequeños cambios podemos utilizar este código para dibujar cualquier elemento SVG:

class elementoSVG {
    // el método constructor
    constructor(elmt, atributos) {
    this.atributos = atributos;
    this.elemento = elmt;
    }
    dibujar() {
      // crea un nuevo elemento svg (this.elemento)
      const elemento = document.createElementNS(SVG_NS, this.elemento);
      // para cada atributo en el objeto atributos
      for (var atributo in this.atributos) {
        if (this.atributos.hasOwnProperty(atributo)) {
          // establece el valor del mismo atributo para el elemento
          elemento.setAttributeNS(null, atributo, this.atributos[atributo]);
        }
      }
      
      // agrega el elemento  al elemento padre, por ejemplo el SVG 
      SVG.appendChild(elemento);
      return elemento;
    }
}
Crear la clase PoligonoRegular

Para crear la clase PoligonoRegular necesitamos dibujar un elemento <polygon>, y para esto podemos utilizar la clase elementoSVG. Sin embargo necesitamos añadir un método que calcule el valor del atributo points del <polygon> y sobrescribir el método constructor. Por lo tanto voy a crear una nueva clase PoligonoRegular que extiende la clase elementoSVG.

class PoligonoRegular extends elementoSVG {
     . . . . .
}

Esto es muy importante: cuando se trata de una clase que extiende otra clase tenemos que utilizar la palabra clave super para invocar la clase padre.

class PoligonoRegular extends elementoSVG {
    constructor(lados, centro, radio) {
      super();
      this.atributos = {}
      this.elemento = "polygon";
      . . . . . . . . 
   
class PoligonoRegular extends elementoSVG {
    constructor(lados, centro, radio) {
      // la palabra clave super se utiliza para invocar la clase padre, ( elementoSVG en este caso ) 
      super();
      
      this.atributos = {}
      this.elemento = "polygon";
      // numero de lados del polígono
      this.lados = lados;
      // el centro del circulo circunscrito
      this.c = centro;
      // el radio del circulo circunscrito
      this.r = radio;
      // construye el atributo points para dibujar un polígono regular
      this.Points = 360 / this.lados;
    }
    
    set Points(a) { // calcula el valor del atributo points del polígono
      this.atributos.points = "";
      for (let i = 1; i <= this.lados; i++) {
        // calcula el ángulo entre los puntos del polígon
        let aRad = Math.PI / 180 * (a * i);
        // calcula las coordenadas en x e y de cada punto
        let Xp = this.c.x + this.r * Math.cos(aRad);
        let Yp = this.c.y + this.r * Math.sin(aRad);
        // construye el atributo points del polígono
        this.atributos.points += Xp + "," + Yp + " ";
      }
    }
  }

Lea más acerca de cómo dibujar polígonos regulares en SVG

Finalmente podemos dibujar círculos, rectángulos, y hexágonos:


let circle = new elementoSVG("circle",{ cx: 70, cy: 100, r: 25, id: "c1" });
  circle.dibujar(SVG);
let rect = new elementoSVG("rect",{ x: 120, y: 130, width: 100, height: 50 });
  rect.dibujar(SVG);
let hexagon = new PoligonoRegular(6, { x: 120, y: 140 }, 60);
  hexagon.dibujar(SVG);

Vea este ejemplo en codepen:

See the Pen ES6 class #2 - extend by Gabi (@enxaneta) on CodePen.