Con la palabra static podemos definir métodos estáticos en Javascript, versión ES6. Explicamos su funcionamiento.
En este artículo vamos a conocer cómo trabajar con miembros de clase en las clases de ES6, la versión reciente del estándar de Javascript. Aprenderemos a crear métodos static y propiedades también, aunque verás que para el caso de las propiedades tendremos que valernos de algún truquillo adicional.
En realidad no es nuestro objetivo introducir el concepto de static, pues es motivo de estudio en detalle en artículos anteriores como Explicaciones de métodos y atributos static. No obstante diremos que los miembros estáticos de las clases en Programación Orientada a Objetos en general son atributos y métodos que dependen directamente de una clase, en vez de depender de un objeto en particular. Al depender de la clase no están asociados a un objeto, por lo que comparten el valor para toda la clase, independientemente de las instancias que se hayan creado.
Ya hemos comentado que con la reciente versión de Javascript ES6 ahora disponemos de clases, aunque no son exactamente lo mismo que en otros lenguajes más tradicionales. En el caso de static no hay cambios en el concepto, pero al no poderse declarar atributos de instancia, tampoco podremos declarar atributos static. Lo veremos también con calma, aunque antes comencemos con los métodos estáticos.
Definir métodos static en ES6
Un método estático se construye simplemente indicando la palabra "static" antes del nombre del método que se está creando. El resto de la definición de un método estático sería igual que la definición de un método convencional, con la excepción de disponer de la variable "this" como habitualmente en los métodos.
En el siguiente ejemplo tenemos una clase llamada "Sumatorio" que tiene un método declarado estático, para sumar los valores de un array.
class Sumatorio {
static sumarArray(arrayValores) {
let suma = 0;
for(let valor of arrayValores){
suma += valor
}
return suma;
}
}
El método static depende directamente de la clase, por lo que usaremos la propia clase para invocarlo.
let suma = Sumatorio.sumarArray([3,4,5]); //suma valdrá 12
Los métodos estáticos pueden servir para muchas cosas. Es el motivo por lo que a veces se usan como un cajón desastre de utilidades que puedan tener que ver con una clase. Pensando en objetos hay que tener cuidado para qué y cómo se utilizan. El pasado ejemplo de sumarArray() no era muy bueno desde la filosofía de la orientación a objetos, pero en el siguiente ejemplo tenemos un método estático un poco mejor pensado.
Tenemos una clase Fecha que nos sirve para crear fechas en Javascript. Es cierto que Javascript contiene ya una clase Date, pero tiene la posibilidad de crear fechas y horas y quizás nosotros solo necesitamos fechas y queremos una serie de utilidades adicionales que no están incluidas en la interfaz original de Date.
En nuestro ejemplo observarás que tenemos un constructor, que recibe el día, mes y año. Sin embargo en la práctica muchas veces las fechas se crean con el día actual. Como no existe la sobrecarga de métodos en Javascript y por tanto tampoco podemos sobrecargar constructores, podríamos echar mano de los métodos static para crear una especie de constructor de la fecha sin parámetros que nos devuelve un objeto Fecha inicializado con el día actual.
class Fecha {
constructor(dia, mes, ano) {
this.dia = dia;
this.mes = mes;
this.ano = ano;
}
static hoy() {
var fecha = new Date();
var dia = fecha.getDate();
var mes = fecha.getMonth() + 1;
var ano = fecha.getFullYear();
return new Fecha(dia, mes, ano);
}
}
Como puedes ver, el método static hoy() se encarga de obtener los valores del día, mes y año actuales e invocar al constructor con tales datos, devolviendo el objeto que se acaba de crear.
Otro ejemplo de método estático o método de clase
Continuamos con un segundo ejemplo de método estático o método de clase. Ahora lo encontramos en el marco de una clase Coordenada,
class Coordenada {
constructor(x, y) {
this.x = x;
this.y = y;
}
static coordenadaOrigen() {
return new Coordenada(0,0);
}
}
En el código anterior tienes un ejemplo de método estático, llamado coordenadaOrigen(), que devuelve una nueva instancia de un objeto de la clase Coordenada, con sus putos x e y igual a cero.
Ese es un método de clase, por lo que tendremos que usar la propia clase para acceder a él.
var origen = Coordenada.coordenadaOrigen();
Atributos estáticos de ECMAScript 2015
La definición de propiedades estáticas, o propiedades de clase, no es tan directa como la definición de métodos estáticos, puesto que en ES6 no se pueden definir propiedades tal como se hace en otros lenguajes de programación más tradicionales.
En ECMAScript 2015 (ES6) tenemos la limitación de no poder declarar atributos en la clase (tenemos que generarlos en el constructor o en los métodos). Esto también se extiende a los atributos de clase o atributos static. Sin embargo, siempre te puedes montar tú mismo algún mecanismo para conseguirlo.
Por ejemplo en el caso de tener atributos de clase estáticos que tengan valores comunes a toda la clase, podríamos hacer uso de los getter, colocando la palabra static a la hora de definir el método get.
class Circulo {
static get pi() {
return 3.1416
}
}
Podremos acceder a "pi" como si fuera una propiedad estática, dependiente directamente de la clase. La usamos directamente desde el nombre de la clase:
console.log(Circulo.pi);
Si lo que queremos es una variable estática, que sea global para toda la clase, con un valor que no depende de las instancias y que puede variar a lo largo del tiempo, podríamos hacer algo como esto:
class Test {
}
Test.variableStatic = 'Algo que guardo en la clase';
Como Javascript es tan permisivo, podemos asociar una propiedad a la clase simplemente asignando cualquier valor. No me gusta demasiado el ejemplo, porque la definición de la propiedad estática estaría fuera del código de la propia clase y por tanto en una lectura a ese código podríamos no darnos cuenta que más adelante se crea esa variable estática.
En el ejemplo típico de crear una variable estática que lleva la cuenta de las instancias creadas a partir de una clase, podríamos optar por algo como esto (que me gusta más por tener la creación de la propiedad estática dentro del constructor).
class Habitante {
constructor(nombre) {
this.nombre = nombre;
if(Habitante.contador) {
Habitante.contador++;
} else {
Habitante.contador = 1;
}
}
}
El problema aquí es que únicamente existirá esa propiedad estática a partir de la primera instanciación de un objeto. Así que otro ejemplo un poco más enrevesado podría ser el siguiente, que hace uso de los getter y de los setter de los objetos Javascript.
class Habitante {
static get contador() {
if(Habitante.contadorPrivado) {
return Habitante.contadorPrivado;
}
return 0;
}
static set contador(valor) {
Habitante.contadorPrivado = valor;
}
constructor(nombre) {
this.nombre = nombre;
if(Habitante.contador) {
Habitante.contador++;
} else {
Habitante.contador = 1;
}
}
}
Como puedes ver, Habitante.contador es nuestra propiedad estática, que estaría disponible gracias a los getter y los setter como si fuera un atributo normal (solo que es estático por estar predecido de "static").
En el constructor hacemos uso de Habitante.contador como si fuera un atributo normal, solo que internamente en la implementación de la clase estas propiedades en realidad se calculan con funciones computadas get y set.
Es un código meramente experimental, pero te puede dar una idea de las cosas que se pueden hacer en Javascript cuando "retuerces" un poco el lenguaje. Aprende más sobre los get y los set en este artículo.
Conclusión sobre los miembros de clase en ES6
Hemos aprendido cosas interesantes sobre la creación de miembros de clase, o miembros estáticos, en las clases de ES6. Como has podido ver, existen algunas particularidades dadas por el lenguaje Javascript, que es importante conocer.
En el siguiente artículo del Manual de ES6 seguiremos hablando de clases, abordando algo tan importante como la herencia de clases en Javascript.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...