Queremos que este proyecto de código abierto esté disponible para personas de todo el mundo.
Ayuda a traducir el contenido de este tutorial a tu idioma!
Propiedades y métodos estáticos.
También podemos asignar un método a la clase como un todo. Dichos métodos se llaman estáticos.
En la declaración de una clase, se preceden por la palabra clave static:
Eso realmente hace lo mismo que asignarlo como una propiedad directamente:
El valor de this en la llamada User.staticMethod() es el mismo constructor de clase User (la regla “objeto antes de punto”).
Por lo general, los métodos estáticos se utilizan para implementar funciones que pertenecen a la clase como un todo, no a un objeto particular de la misma.
Por ejemplo, tenemos objetos Article y necesitamos una función para compararlos.
Una solución natural sería agregar el método Article.compare:
class Article {
constructor(title, date) {
this.title = title;
this.date = date;
}
static compare(articleA, articleB) {
return articleA.date - articleB.date;
}
}
// uso
let articles = [
new Article("HTML", new Date(2019, 1, 1)),
new Article("CSS", new Date(2019, 0, 1)),
new Article("JavaScript", new Date(2019, 11, 1))
];
articles.sort(Article.compare);
alert( articles[0].title ); // CSS
Aquí el método Article.compare se encuentra “encima” de los artículos, como un medio para compararlos. No es el método de un artículo sino de toda la clase.
Otro ejemplo sería un método llamado “factory”.
Digamos que necesitamos múltiples formas de crear un artículo:
- Crearlo por parámetros dados (
title,dateetc.). - Crear un artículo vacío con la fecha de hoy.
- … o cualquier otra manera.
La primera forma puede ser implementada por el constructor. Y para la segunda podemos hacer un método estático de la clase.
Tal como Article.createTodays() aquí:
Ahora, cada vez que necesitamos crear un resumen de hoy, podemos llamar a Article.createTodays(). Una vez más, ese no es el método de un objeto artículo, sino el método de toda la clase.
Los métodos estáticos también se utilizan en clases relacionadas con base de datos para buscar/guardar/eliminar entradas de la misma, como esta:
// suponiendo que Article es una clase especial para gestionar artículos
// método estático para eliminar el artículo por id:
Article.remove({id: 12345});
Los métodos estáticos son llamados sobre las clases, no sobre los objetos individuales.
Por ejemplo, este código no funcionará:
// ...
article.createTodays(); /// Error: article.createTodays is not a function
Propiedades estáticas
Las propiedades estáticas también son posibles, se ven como propiedades de clase regular, pero precedidas por static:
Eso es lo mismo que una asignación directa a Article:
Article.publisher = "Ilya Kantor";
Herencia de propiedades y métodos estáticos
Las propiedades y métodos estáticos son heredados.
Por ejemplo, Animal.compare y Animal.planet en el siguiente código son heredados y accesibles como Rabbit.compare y Rabbit.planet:
class Animal {
static planet = "Tierra";
constructor(name, speed) {
this.speed = speed;
this.name = name;
}
run(speed = 0) {
this.speed += speed;
alert(`${this.name} corre a una velocidad de ${this.speed}.`);
}
static compare(animalA, animalB) {
return animalA.speed - animalB.speed;
}
}
// Hereda de Animal
class Rabbit extends Animal {
hide() {
alert(`${this.name} se esconde!`);
}
}
let rabbits = [
new Rabbit("Conejo Blanco", 10),
new Rabbit("Conejo Negro", 5)
];
rabbits.sort(Rabbit.compare);
rabbits[0].run(); // Conejo Negro corre a una velocidad de 5.
alert(Rabbit.planet); // Tierra
Ahora, cuando llamemos a Rabbit.compare, se llamará a Animal.compare heredado.
¿Como funciona? Nuevamente, usando prototipos. Como ya habrás adivinado, extends da a Rabbit el [[Prototype]] referente a Animal.
Entonces, Rabbit extends Animal crea dos referencias [[Prototype]]:
- La función de
Rabbitse hereda prototípicamente de la función deAnimal. Rabbit.prototypeprototípicamente hereda deAnimal.prototype.
Como resultado, la herencia funciona tanto para métodos regulares como estáticos.
Verifiquemos eso por código, aquí:
Resumen
Los métodos estáticos se utilizan en la funcionalidad propia de la clase “en su conjunto”. No se relaciona con una instancia de clase concreta.
Por ejemplo, un método para comparar Article.compare (article1, article2) o un método de fábrica Article.createTodays().
Están etiquetados por la palabra static en la declaración de clase.
Las propiedades estáticas se utilizan cuando queremos almacenar datos a nivel de clase, también no vinculados a una instancia.
La sintaxis es:
class MyClass {
static property = ...;
static method() {
...
}
}
Técnicamente, la declaración estática es lo mismo que asignar a la clase misma:
MyClass.property = ...
MyClass.method = ...
Las propiedades y métodos estáticos se heredan.
Para class B extends A el prototipo de la clase B en sí mismo apunta a A: B.[[Prototipo]] = A. Entonces, si no se encuentra un campo en B, la búsqueda continúa en A.
Tareas
Como sabemos, todos los objetos normalmente heredan de Object.prototype y obtienen acceso a métodos de objeto “genéricos” como hasOwnProperty etc.
Por ejemplo:
Pero si lo escribimos explícitamente como "class Rabbit extends Object", entonces ¿el resultado sería diferente de una simple "class Rabbit"?
¿Cuál es la diferencia?
Aquí un ejemplo de dicho código (no funciona – ¿por qué? ¿Arréglalo?):
class Rabbit extends Object {
constructor(name) {
this.name = name;
}
}
let rabbit = new Rabbit("Rab");
alert( rabbit.hasOwnProperty('name') ); // Error
Primero, veamos por qué el código anterior no funciona.
La razón se vuelve evidente si intentamos ejecutarlo. Un constructor de clase heredado tiene que llamar a super(). De lo contrario "this" no será “definido”.
Así que aquí está la solución:
Pero eso no es todo.
Incluso después de arreglarlo, aún existe una diferencia importante entre "class Rabbit extends Object" y class Rabbit.
Como sabemos, la sintaxis “extends” configura dos prototipos:
- Entre
"prototype"de las funciones del constructor (para métodos). - Entre las propias funciones del constructor (para métodos estáticos).
En el caso de class Rabbit extends Object significa:
Entonces Rabbit ahora proporciona acceso a los métodos estáticos de Object a través de Rabbit, así:
Pero si no tenemos extends Object, entonces Rabbit.__proto__ no está definido como Object.
Aquí está la demostración:
class Rabbit {}
alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) verdadero
alert( Rabbit.__proto__ === Object ); // (2) falso (!)
alert( Rabbit.__proto__ === Function.prototype ); // como cualquier función por defecto
// error, no existe esta función en Rabbit
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error
Entonces Rabbit no proporciona acceso a métodos estáticos de Object en este caso.
Por cierto, Function.prototype también tiene métodos de función “genéricos”, como call, bind etc. Finalmente, están disponibles en ambos casos, por el Object que tiene el constructor incorporado Object.__proto__ === Function.prototype.
Aquí está la imagen:
Por lo tanto, en pocas palabras, existen dos diferencias:
| class Rabbit | class Rabbit extends Object |
|---|---|
| – | necesita llamar a super() en el constructor |
Rabbit.__proto__ === Function.prototype |
Rabbit.__proto__ === Object |
- © 2007—2026 Ilya Kantor
- acerca del proyecto
- contáctenos
Comentarios
<code>, para varias líneas – envolverlas en la etiqueta<pre>, para más de 10 líneas – utilice una entorno controlado (sandbox) (plnkr, jsbin, codepen…)