[ Web Proxy ]
URL:
Viewing: https://javascript.info/static-properties-methods [Back]  [Original]

Static properties and methods
EN

We want to make this open-source project available for people all around the world.

Help to translate the content of this tutorial to your language!

    Search on Javascript.info:
    Search in the tutorial:
    Light themeDark theme
    DanskEnglishEspaolFranaisIndonesiaItalianoTrkeOzbek

    Static properties and methods

    We can also assign a method to the class as a whole. Such methods are called static.

    In a class declaration, they are prepended by static keyword, like this:

    class User {
      static staticMethod() {
        alert(this === User);
      }
    }
    
    User.staticMethod(); // true

    That actually does the same as assigning it as a property directly:

    class User { }
    
    User.staticMethod = function() {
      alert(this === User);
    };
    
    User.staticMethod(); // true

    The value of this in User.staticMethod() call is the class constructor User itself (the object before dot rule).

    Usually, static methods are used to implement functions that belong to the class as a whole, but not to any particular object of it.

    For instance, we have Article objects and need a function to compare them.

    A natural solution would be to add Article.compare static method:

    class Article {
      constructor(title, date) {
        this.title = title;
        this.date = date;
      }
    
      static compare(articleA, articleB) {
        return articleA.date - articleB.date;
      }
    }
    
    // usage
    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

    Here Article.compare method stands above articles, as a means to compare them. Its not a method of an article, but rather of the whole class.

    Another example would be a so-called factory method.

    Lets say, we need multiple ways to create an article:

    1. Create by given parameters (title, date etc).
    2. Create an empty article with todays date.
    3. or else somehow.

    The first way can be implemented by the constructor. And for the second one we can make a static method of the class.

    Such as Article.createTodays() here:

    class Article {
      constructor(title, date) {
        this.title = title;
        this.date = date;
      }
    
      static createTodays() {
        // remember, this = Article
        return new this("Today's digest", new Date());
      }
    }
    
    let article = Article.createTodays();
    
    alert( article.title ); // Today's digest

    Now every time we need to create a todays digest, we can call Article.createTodays(). Once again, thats not a method of an article, but a method of the whole class.

    Static methods are also used in database-related classes to search/save/remove entries from the database, like this:

    // assuming Article is a special class for managing articles
    // static method to remove the article by id:
    Article.remove({id: 12345});
    Static methods arent available for individual objects

    Static methods are callable on classes, not on individual objects.

    E.g. such code wont work:

    // ...
    article.createTodays(); /// Error: article.createTodays is not a function

    Static properties

    A recent addition
    This is a recent addition to the language. Examples work in the recent Chrome.

    Static properties are also possible, they look like regular class properties, but prepended by static:

    class Article {
      static publisher = "Ilya Kantor";
    }
    
    alert( Article.publisher ); // Ilya Kantor

    That is the same as a direct assignment to Article:

    Article.publisher = "Ilya Kantor";

    Inheritance of static properties and methods

    Static properties and methods are inherited.

    For instance, Animal.compare and Animal.planet in the code below are inherited and accessible as Rabbit.compare and Rabbit.planet:

    class Animal {
      static planet = "Earth";
    
      constructor(name, speed) {
        this.speed = speed;
        this.name = name;
      }
    
      run(speed = 0) {
        this.speed += speed;
        alert(`${this.name} runs with speed ${this.speed}.`);
      }
    
      static compare(animalA, animalB) {
        return animalA.speed - animalB.speed;
      }
    
    }
    
    // Inherit from Animal
    class Rabbit extends Animal {
      hide() {
        alert(`${this.name} hides!`);
      }
    }
    
    let rabbits = [
      new Rabbit("White Rabbit", 10),
      new Rabbit("Black Rabbit", 5)
    ];
    
    rabbits.sort(Rabbit.compare);
    
    rabbits[0].run(); // Black Rabbit runs with speed 5.
    
    alert(Rabbit.planet); // Earth

    Now when we call Rabbit.compare, the inherited Animal.compare will be called.

    How does it work? Again, using prototypes. As you might have already guessed, extends gives Rabbit the [[Prototype]] reference to Animal.

    []

    So, Rabbit extends Animal creates two [[Prototype]] references:

    1. Rabbit function prototypally inherits from Animal function.
    2. Rabbit.prototype prototypally inherits from Animal.prototype.

    As a result, inheritance works both for regular and static methods.

    Here, lets check that by code:

    class Animal {}
    class Rabbit extends Animal {}
    
    // for statics
    alert(Rabbit.__proto__ === Animal); // true
    
    // for regular methods
    alert(Rabbit.prototype.__proto__ === Animal.prototype); // true

    Summary

    Static methods are used for the functionality that belongs to the class as a whole. It doesnt relate to a concrete class instance.

    For example, a method for comparison Article.compare(article1, article2) or a factory method Article.createTodays().

    They are labeled by the word static in class declaration.

    Static properties are used when wed like to store class-level data, also not bound to an instance.

    The syntax is:

    class MyClass {
      static property = ...;
    
      static method() {
        ...
      }
    }

    Technically, static declaration is the same as assigning to the class itself:

    MyClass.property = ...
    MyClass.method = ...

    Static properties and methods are inherited.

    For class B extends A the prototype of the class B itself points to A: B.[[Prototype]] = A. So if a field is not found in B, the search continues in A.

    Tasks

    importance: 3

    As we know, all objects normally inherit from Object.prototype and get access to generic object methods like hasOwnProperty etc.

    For instance:

    class Rabbit {
      constructor(name) {
        this.name = name;
      }
    }
    
    let rabbit = new Rabbit("Rab");
    
    // hasOwnProperty method is from Object.prototype
    alert( rabbit.hasOwnProperty('name') ); // true

    But if we spell it out explicitly like "class Rabbit extends Object", then the result would be different from a simple "class Rabbit"?

    Whats the difference?

    Heres an example of such code (it doesnt work why? fix it?):

    class Rabbit extends Object {
      constructor(name) {
        this.name = name;
      }
    }
    
    let rabbit = new Rabbit("Rab");
    
    alert( rabbit.hasOwnProperty('name') ); // Error
    solution

    First, lets see why the latter code doesnt work.

    The reason becomes obvious if we try to run it. An inheriting class constructor must call super(). Otherwise "this" wont be defined.

    So heres the fix:

    class Rabbit extends Object {
      constructor(name) {
        super(); // need to call the parent constructor when inheriting
        this.name = name;
      }
    }
    
    let rabbit = new Rabbit("Rab");
    
    alert( rabbit.hasOwnProperty('name') ); // true

    But thats not all yet.

    Even after the fix, theres still an important difference between "class Rabbit extends Object" and class Rabbit.

    As we know, the extends syntax sets up two prototypes:

    1. Between "prototype" of the constructor functions (for methods).
    2. Between the constructor functions themselves (for static methods).

    In the case of class Rabbit extends Object it means:

    class Rabbit extends Object {}
    
    alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
    alert( Rabbit.__proto__ === Object ); // (2) true

    So Rabbit now provides access to the static methods of Object via Rabbit, like this:

    class Rabbit extends Object {}
    
    // normally we call Object.getOwnPropertyNames
    alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b

    But if we dont have extends Object, then Rabbit.__proto__ is not set to Object.

    Heres the demo:

    class Rabbit {}
    
    alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
    alert( Rabbit.__proto__ === Object ); // (2) false (!)
    alert( Rabbit.__proto__ === Function.prototype ); // as any function by default
    
    // error, no such function in Rabbit
    alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error

    So Rabbit doesnt provide access to static methods of Object in that case.

    By the way, Function.prototype also has generic function methods, like call, bind etc. They are ultimately available in both cases, because for the built-in Object constructor, Object.__proto__ === Function.prototype.

    Heres the picture:

    []

    So, to put it short, there are two differences:

    class Rabbit class Rabbit extends Object
    needs to call super() in constructor
    Rabbit.__proto__ === Function.prototype Rabbit.__proto__ === Object
    Tutorial map

    Comments

    read this before commenting
    • If you have suggestions what to improve - please submit a GitHub issue or a pull request instead of commenting.
    • If you can't understand something in the article please elaborate.
    • To insert few words of code, use the <code> tag, for several lines wrap them in <pre> tag, for more than 10 lines use a sandbox (plnkr, jsbin, codepen)

    Web Proxy Viewer  |  New URL  |  Original Page