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

Class basic syntax
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

    Class basic syntax

    In object-oriented programming, a class is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions or methods).

    Wikipedia

    In practice, we often need to create many objects of the same kind, like users, or goods or whatever.

    As we already know from the chapter Constructor, operator "new", new function can help with that.

    But in the modern JavaScript, theres a more advanced class construct, that introduces great new features which are useful for object-oriented programming.

    The class syntax

    The basic syntax is:

    class MyClass {
      // class methods
      constructor() { ... }
      method1() { ... }
      method2() { ... }
      method3() { ... }
      ...
    }

    Then use new MyClass() to create a new object with all the listed methods.

    The constructor() method is called automatically by new, so we can initialize the object there.

    For example:

    class User {
    
      constructor(name) {
        this.name = name;
      }
    
      sayHi() {
        alert(this.name);
      }
    
    }
    
    // Usage:
    let user = new User("John");
    user.sayHi();

    When new User("John") is called:

    1. A new object is created.
    2. The constructor runs with the given argument and assigns it to this.name.

    Then we can call object methods, such as user.sayHi().

    No comma between class methods

    A common pitfall for novice developers is to put a comma between class methods, which would result in a syntax error.

    The notation here is not to be confused with object literals. Within the class, no commas are required.

    What is a class?

    So, what exactly is a class? Thats not an entirely new language-level entity, as one might think.

    Lets unveil any magic and see what a class really is. Thatll help in understanding many complex aspects.

    In JavaScript, a class is a kind of function.

    Here, take a look:

    class User {
      constructor(name) { this.name = name; }
      sayHi() { alert(this.name); }
    }
    
    // proof: User is a function
    alert(typeof User); // function

    What class User {...} construct really does is:

    1. Creates a function named User, that becomes the result of the class declaration. The function code is taken from the constructor method (assumed empty if we dont write such method).
    2. Stores class methods, such as sayHi, in User.prototype.

    After new User object is created, when we call its method, its taken from the prototype, just as described in the chapter F.prototype. So the object has access to class methods.

    We can illustrate the result of class User declaration as:

    []

    Heres the code to introspect it:

    class User {
      constructor(name) { this.name = name; }
      sayHi() { alert(this.name); }
    }
    
    // class is a function
    alert(typeof User); // function
    
    // ...or, more precisely, the constructor method
    alert(User === User.prototype.constructor); // true
    
    // The methods are in User.prototype, e.g:
    alert(User.prototype.sayHi); // the code of the sayHi method
    
    // there are exactly two methods in the prototype
    alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi

    Not just a syntactic sugar

    Sometimes people say that class is a syntactic sugar (syntax that is designed to make things easier to read, but doesnt introduce anything new), because we could actually declare the same thing without using the class keyword at all:

    // rewriting class User in pure functions
    
    // 1. Create constructor function
    function User(name) {
      this.name = name;
    }
    // a function prototype has "constructor" property by default,
    // so we don't need to create it
    
    // 2. Add the method to prototype
    User.prototype.sayHi = function() {
      alert(this.name);
    };
    
    // Usage:
    let user = new User("John");
    user.sayHi();

    The result of this definition is about the same. So, there are indeed reasons why class can be considered a syntactic sugar to define a constructor together with its prototype methods.

    Still, there are important differences.

    1. First, a function created by class is labelled by a special internal property [[IsClassConstructor]]: true. So its not entirely the same as creating it manually.

      The language checks for that property in a variety of places. For example, unlike a regular function, it must be called with new:

      class User {
        constructor() {}
      }
      
      alert(typeof User); // function
      User(); // Error: Class constructor User cannot be invoked without 'new'

      Also, a string representation of a class constructor in most JavaScript engines starts with the class

      class User {
        constructor() {}
      }
      
      alert(User); // class User { ... }

      There are other differences, well see them soon.

    2. Class methods are non-enumerable. A class definition sets enumerable flag to false for all methods in the "prototype".

      Thats good, because if we for..in over an object, we usually dont want its class methods.

    3. Classes always use strict. All code inside the class construct is automatically in strict mode.

    Besides, class syntax brings many other features that well explore later.

    Class Expression

    Just like functions, classes can be defined inside another expression, passed around, returned, assigned, etc.

    Heres an example of a class expression:

    let User = class {
      sayHi() {
        alert("Hello");
      }
    };

    Similar to Named Function Expressions, class expressions may have a name.

    If a class expression has a name, its visible inside the class only:

    // "Named Class Expression"
    // (no such term in the spec, but that's similar to Named Function Expression)
    let User = class MyClass {
      sayHi() {
        alert(MyClass); // MyClass name is visible only inside the class
      }
    };
    
    new User().sayHi(); // works, shows MyClass definition
    
    alert(MyClass); // error, MyClass name isn't visible outside of the class

    We can even make classes dynamically on-demand, like this:

    function makeClass(phrase) {
      // declare a class and return it
      return class {
        sayHi() {
          alert(phrase);
        }
      };
    }
    
    // Create a new class
    let User = makeClass("Hello");
    
    new User().sayHi(); // Hello

    Getters/setters

    Just like literal objects, classes may include getters/setters, computed properties etc.

    Heres an example for user.name implemented using get/set:

    class User {
    
      constructor(name) {
        // invokes the setter
        this.name = name;
      }
    
      get name() {
        return this._name;
      }
    
      set name(value) {
        if (value.length < 4) {
          alert("Name is too short.");
          return;
        }
        this._name = value;
      }
    
    }
    
    let user = new User("John");
    alert(user.name); // John
    
    user = new User(""); // Name is too short.

    Technically, such class declaration works by creating getters and setters in User.prototype.

    Computed names []

    Heres an example with a computed method name using brackets [...]:

    class User {
    
      ['say' + 'Hi']() {
        alert("Hello");
      }
    
    }
    
    new User().sayHi();

    Such features are easy to remember, as they resemble that of literal objects.

    Class fields

    Old browsers may need a polyfill

    Class fields are a recent addition to the language.

    Previously, our classes only had methods.

    Class fields is a syntax that allows to add any properties.

    For instance, lets add name property to class User:

    class User {
      name = "John";
    
      sayHi() {
        alert(`Hello, ${this.name}!`);
      }
    }
    
    new User().sayHi(); // Hello, John!

    So, we just write = in the declaration, and thats it.

    The important difference of class fields is that they are set on individual objects, not User.prototype:

    class User {
      name = "John";
    }
    
    let user = new User();
    alert(user.name); // John
    alert(User.prototype.name); // undefined

    We can also assign values using more complex expressions and function calls:

    class User {
      name = prompt("Name, please?", "John");
    }
    
    let user = new User();
    alert(user.name); // John

    Making bound methods with class fields

    As demonstrated in the chapter Function binding functions in JavaScript have a dynamic this. It depends on the context of the call.

    So if an object method is passed around and called in another context, this wont be a reference to its object any more.

    For instance, this code will show undefined:

    class Button {
      constructor(value) {
        this.value = value;
      }
    
      click() {
        alert(this.value);
      }
    }
    
    let button = new Button("hello");
    
    setTimeout(button.click, 1000); // undefined

    The problem is called losing this.

    There are two approaches to fixing it, as discussed in the chapter Function binding:

    1. Pass a wrapper-function, such as setTimeout(() => button.click(), 1000).
    2. Bind the method to object, e.g. in the constructor.

    Class fields provide another, quite elegant syntax:

    class Button {
      constructor(value) {
        this.value = value;
      }
      click = () => {
        alert(this.value);
      }
    }
    
    let button = new Button("hello");
    
    setTimeout(button.click, 1000); // hello

    The class field click = () => {...} is created on a per-object basis, theres a separate function for each Button object, with this inside it referencing that object. We can pass button.click around anywhere, and the value of this will always be correct.

    Thats especially useful in browser environment, for event listeners.

    Summary

    The basic class syntax looks like this:

    class MyClass {
      prop = value; // property
    
      constructor(...) { // constructor
        // ...
      }
    
      method(...) {} // method
    
      get something(...) {} // getter method
      set something(...) {} // setter method
    
      [Symbol.iterator]() {} // method with computed name (symbol here)
      // ...
    }

    MyClass is technically a function (the one that we provide as constructor), while methods, getters and setters are written to MyClass.prototype.

    In the next chapters well learn more about classes, including inheritance and other features.

    Tasks

    importance: 5

    The Clock class (see the sandbox) is written in functional style. Rewrite it in the class syntax.

    P.S. The clock ticks in the console, open it to see.

    Open a sandbox for the task.

    solution
    class Clock {
      constructor({ template }) {
        this.template = template;
      }
    
      render() {
        let date = new Date();
    
        let hours = date.getHours();
        if (hours < 10) hours = '0' + hours;
    
        let mins = date.getMinutes();
        if (mins < 10) mins = '0' + mins;
    
        let secs = date.getSeconds();
        if (secs < 10) secs = '0' + secs;
    
        let output = this.template
          .replace('h', hours)
          .replace('m', mins)
          .replace('s', secs);
    
        console.log(output);
      }
    
      stop() {
        clearInterval(this.timer);
      }
    
      start() {
        this.render();
        this.timer = setInterval(() => this.render(), 1000);
      }
    }
    
    
    let clock = new Clock({template: 'h:m:s'});
    clock.start();

    Open the solution in a sandbox.

    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