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
Nota: El hecho de no poder disponer de "this" dentro de un método estático es debido a que el método no se invoca con relación a ningún objeto. Como has visto, usamos el nombre de la clase para invocarlo y no un objeto instanciado. Como sabes, "this" tiene una referencia al objeto donde se lanzó un mensaje (el objeto sobre el que se invocó un método). Como no existe tal objeto de invocación, no existe un objeto en "this". En principio podríamos pensar que "this" entonces valdrá "undefined", pero lo que hay en realidad es el código de la propia clase.
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.
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 Person { constructor(firstname, lastname, birthday) { this.firstname = firstname; this.lastname = lastname; this.birthday = new Date(birthday); } get age() { var ageDifMs = Date.now() - this.birthday.getTime(); var ageDate = new Date(ageDifMs); return Math.abs(ageDate.getUTCFullYear() - 1970); } }
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.
<!DOCTYPE html> <html> <head> <title>Ejemplo de JavaScript</title> <meta charset="UTF-8"> </head> <body> <script> class Person { constructor(firstname, lastname, birthday) { this.firstname = firstname; this.lastname = lastname; this.birthday = new Date(birthday); } get age() { var ageDifMs = Date.now() - this.birthday.getTime(); var ageDate = new Date(ageDifMs); return Math.abs(ageDate.getUTCFullYear() - 1970); } } var p = new Person('Pablo', ' Almunia', 30); document.write(p.firstname, p.lastname); document.write("<br>"); document.write(p.age); </script> </body> </html>
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.
Con esta nueva actualización de JavaScript tenemos la posibilidad de definir métodos estáticos en una clase. Un método estático pertenece a la clase y no a una instancia u objeto de una clase. Para llamar luego a un método estático lo hacemos directamente antecediendo el nombre de la clase.
Se las utiliza para definir funcionalidades que pertenecen a una clase pero que no dependan de instancias de dicha clase. Los métodos estáticos no pueden acceder a los atributos de la clase mediante this.
Problema
Plantear una clase llamada 'Matematica' que defina una serie de métodos estáticos que nos retorne el primero el mayor de dos enteros y el segundo el menor. Además agregar un tercer método estático que retorne un valor aleatorio entre dos valores enteros que le enviamos como parámetros.
<!DOCTYPE html> <html> <head> <title>Ejemplo de JavaScript</title> <meta charset="UTF-8"> </head> <body> <script> class Matematica { static mayor(v1,v2) { if (v1>v2) return v1; else return v2; } static menor(v1,v2) { if (v1<v2) return v1; else return v2; } static aleatorio(inicio, fin) { return Math.trunc((Math.random()*(fin+1-inicio))+inicio); } } let x1=Matematica.aleatorio(1,10); let x2=Matematica.aleatorio(1,10); document.write(`El mayor entre ${x1} y ${x2} es ${Matematica.mayor(x1,x2)} <br>`); document.write(`El menor entre ${x1} y ${x2} es ${Matematica.menor(x1,x2)}`); </script> </body> </html>
Utilizamos la palabra clave 'static' para definir éste tipo de métodos:
static mayor(v1,v2) { if (v1>v2) return v1; else return v2; }
Un método estático normalmente implementa un algoritmo para procesar los datos que llegan como parámetro.
Para la llamada a un método estático lo hacemos antecediendo el nombre de la clase y no un objeto de la clase:
let x1=Matematica.aleatorio(1,10);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.