Clases
Contenidos
¿Qué son las clases?
JavaScript ofrece muchos paradigmas de programación y la programación orientada a objetos (OOP ó POO) es uno de ellos. Todo en JavaScript es un objeto, incluidas las funciones. JavaScript ES6 agregó soporte para la sintaxis de clase.
Una clase es una plantilla o modelo para definir una estructura y un comportamiento compartidos entre objetos similares.
class CircleES6 { constructor(x, y, radius) { this.x = x this.y = y this.radius = radius } move(x, y) { this.x = x this.y = y } area() { return Math.PI * Math.pow(this.radius, 2) } } const circleES6 = new CircleES6(0,0,5) console.log(circleES6.x, circleES6.y, circleES6.area()) circleES6.move(3,5) console.log(circleES6.x, circleES6.y, circleES6.area())
Herencia o extensión a partir de una clase
Podemos definir nuevas clases, hacer que amplíen las otras clases e instanciar objetos a partir de ellas utilizando la nueva palabra clave.
Podemos personalizar la construcción de cada objeto y definir funciones compartidas entre estos objetos. Aquí hay un ejemplo de clase estándar que demuestra todas estas características.
class CircleES6 { constructor(x, y, radius) { this.x = x this.y = y this.radius = radius } move(x, y) { this.x = x this.y = y } area() { return Math.PI * Math.pow(this.radius, 2) } } class ColoredCircleES6 extends CircleES6 { constructor(x, y, radius, color) { super(x,y,radius) this.color = color; } area(){ return `${Math.PI * Math.pow(this.radius, 2)} color ${this.color}` } } const circleES6 = new CircleES6(0,0,5) const coloredCircleES6 = new ColoredCircleES6(0,0,5,'Orange') const coloredCircleES6Two = new ColoredCircleES6(1,1,3,'Blue') coloredCircleES6Two.area = () => "I'm unique" console.log(circleES6.area()) console.log(coloredCircleES6.area()) console.log(coloredCircleES6Two.area())
Tenemos una clase de CircleES6
y una clase de ColoredCircleES6
que amplía la clase de CircleES6
. Cada círculo coloreado es también una círculo. Ambas clases definen una función constructora. La función constructora es especial y se llama cada vez que creamos una instancia de un objeto fuera de la clase, lo que hacemos usando la nueva palabra clave new
.
Luego instanciamos un objeto de la clase CircleES6
y otros dos objetos de la clase ColoredCircleES6
. Los argumentos que pasamos aquí cuando instanciamos estos objetos son accesibles en la función constructora de la clase.
La clase CircleES6
espera tres argumentos, la posición en el eje X, eje Y y el radio . Almacena el valor en la instancia, usando la palabra clave this
, y la clase ColoredCircleES6
espera los mismos argumentos más el color.
Dado que amplía la clase CircleES6
, llamará al super
con los argumentos correspondientes y almacena el valor del color en su instancia.
En el tercer objeto, que instanciamos aquí desde la clase ColoredCircleES6
, también definimos una función de area()
directamente en el objeto. Cuando probemos este script, circleES6
usará el método de area()
de su clase, la clase CircleES6
, coloredCircleES6
usará el método de area()
de la clase ColoredCircleES6
y coloredCircleES6Two
usará su propio método de area()
definido directamente.
Métodos
Para definir métodos simplemente escribimos el nombre y unos paréntesis para definir los argumentos, como en el ejemplo anterior. Al igual que en otros lenguajes de programación que tienen soporte para clases, también podemos definir métodos utilizando las palabras claves get
, set
y static
.
Getter
Un getter es un método que se utiliza para obtener el valor de una propiedad de una clase. En el caso de la clase CircleES6, podríamos utilizar un getter para obtener el valor de la propiedad radius. Para crear un getter, se utiliza la palabra clave get
seguida del nombre del método:
class CircleES6 { constructor(x, y, radius) { this.x = x; this.y = y; this._radius = radius; } get radius() { return this._radius; } move(x, y) { this.x = x; this.y = y; } area() { return Math.PI * Math.pow(this._radius, 2); } } const circle = new CircleES6(0, 0, 5); console.log(circle.radius); // 5
Setter
Un setter es un método que se utiliza para modificar el valor de una propiedad de una clase. En el caso de la clase CircleES6, podríamos utilizar un setter para modificar el valor de la propiedad radius. Para crear un setter, se utiliza la palabra clave set
seguida del nombre del método:
class CircleES6 { constructor(x, y, radius) { this.x = x; this.y = y; this._radius = radius; } get radius() { return this._radius; } set radius(value) { if (value <= 0) { throw new Error("El radio debe ser un número positivo"); } this._radius = value; } move(x, y) { this.x = x; this.y = y; } area() { return Math.PI * Math.pow(this._radius, 2); } } const circle = new CircleES6(0, 0, 5); console.log(circle.radius); // 5 circle.radius = 10; console.log(circle.radius); // 10
En este ejemplo, hemos creado un setter para la propiedad radius
que comprueba que el valor que se está intentando establecer es mayor que cero. Si el valor es menor o igual que cero, se lanza un error.
Static
Un método estático es un método que pertenece a la clase en sí misma, no a las instancias de la clase. En otras palabras, se puede acceder a un método estático sin necesidad de crear una instancia de la clase. En el caso de la clase CircleES6
, podríamos utilizar un método estático para calcular el diámetro de un círculo. Para crear un método estático, se utiliza la palabra clave static
seguida del nombre del método:
class CircleES6 { constructor(x, y, radius) { this.x = x; this.y = y; this.radius = radius; } move(x, y) { this.x = x; this.y = y; } area() { return Math.PI * Math.pow(this.radius, 2); } static diameter(radius) { return radius * 2; } } console.log(CircleES6.diameter(5));
En este ejemplo, hemos creado un miembro estático llamado diameter
, que calcula el diámetro de un círculo a partir del radio que se le pase como parámetro. Para llamar al método diameter
, utilizamos el nombre de la clase seguido del nombre del método estático, en lugar de crear una instancia de la clase.
Variables y bloques estáticos
Por ejemplo, si quisiéramos inicializar un miembro estático llamado “defaultRadius” en la clase CircleES6, podríamos hacerlo de la siguiente manera:
Variables estáticas
class CircleES6 { static defaultRadius = 10; constructor(x, y, radius) { this.x = x; this.y = y; this.radius = radius || CircleES6.defaultRadius; } move(x, y) { this.x = x; this.y = y; } area() { return Math.PI * Math.pow(this.radius, 2); } static diameter(radius) { return radius * 2; } } console.log(new CircleES6(0, 0, 5).radius) console.log(new CircleES6(0, 0).radius)
En este ejemplo, hemos inicializado un miembro estático llamado defaultRadius
con un valor de 10
. Este miembro estático se utiliza en el constructor de la clase para establecer el radio predeterminado de un círculo si no se proporciona uno en el momento de la creación. Podemos ver que el valor predeterminado se utiliza cuando se crea una instancia de la clase sin un radio especificado.
Es importante tener en cuenta que la inicialización de miembros estáticos solo está disponible en las versiones más recientes de JavaScript (a partir de ES6). Si se está utilizando una versión anterior de JavaScript, esta sintaxis no funcionará.
Bloque estático
si deseamos realizar algún tipo de inicialización o configuración compleja para los miembros estáticos, podemos hacerlo en el contexto de una función estática que se ejecuta una vez en el momento de la declaración de la clase. Esta técnica se conoce como “patrón IIFE” (Immediately Invoked Function Expression) y puede ser utilizada para inicializar miembros estáticos.
Aquí te dejo un ejemplo utilizando la clase CircleES6:
En este ejemplo, hemos creado una función estática llamada init
que se ejecuta una vez cuando se declara la clase. La función establece el miembro estático defaultRadius
a un valor aleatorio entre un mínimo y un máximo establecidos. Luego, llamamos a la función “init” inmediatamente después de declarar la clase para inicializar el miembro estático.
Es importante tener en cuenta que la técnica de patrón IIFE no se usa comúnmente en JavaScript para inicializar miembros estáticos, ya que los miembros estáticos se pueden inicializar en el momento de la declaración. Sin embargo, puede ser útil para realizar configuraciones más complejas o para realizar tareas adicionales al inicializar miembros estáticos.
Esta última funcionalidad de bloque estático de inicialización está disponible desde ECMAScript 2022. Consulta si tu intérprete de JavaScript soporta esta funcionalidad