JavaScript - tutorial - 26 - classes

revision:


Content

JavaScript classes are templates for JavaScript objects. The body of a class is the part that is in curly brackets {}. Class inheritance Static methods Class examples

JavaScript classes are templates for JavaScript objects.

top

ECMAScript 2015, also known as ES6, introduced JavaScript Classes. They encapsulate data with code to work on that data. Classes in JS are built on prototypes but also have some syntax and semantics that are unique to classes.

Syntax:

class ClassName {
                    constructor() { ... } 
                } 

class ClassName {
                    constructor() { ... } 
                    method_1() {...}
                    method_2() {...}
                    method_3() {...}
                    } 
        

Classes are in fact "special functions", and just as you can define function expressions and function declarations, a class can be defined in two ways: a class expression or a class declaration.

declaration

            class Rectangle {
                constructor(height, width) {
                    this.height = height;
                    this.width = width;
                }
            }
            

expression: anonymous or with name

                const Rectangle = class {
                    constructor(height, width) {
                      this.height = height;
                      this.width = width;
                    }
                  };

                  const Rectangle = class Rectangle2 {
                    constructor(height, width) {
                      this.height = height;
                      this.width = width;
                    }
                  };
                  
            

create a class : use the keyword "class" to create a class;

Always add a method named constructor().

            class Car {
                constructor(name, year) {
                    this.name = name;
                    this.year = year;
                }
            } 
    

When you have a class, you can use the class to create objects. The constructor method is called automatically when a new object is created.

Examples

example: how to use a JavaScript Class.

code:
                    <div class="spec">
                        <a id="demo"></a>
                    </div>
                    <script>
                        class Car {
                            constructor(name, year) {
                                this.name = name;
                                this.year = year;
                            }
                        }
                        myCar = new Car("Ford", 2014);
                        document.getElementById("demo").innerHTML =   myCar.name + " " + myCar.year;
                    </script>
                

the constructor method is a special method

It has to have the exact name "constructor"; it is executed automatically when a new object is created; it is used to initialize object properties.
If you do not define a constructor method, JavaScript will add an empty constructor method.

Examples

example: pass a parameter into the "age()" method.

code:
                    <div class="spec">
                        <a id="demo2"></a>         
                    </div>
                    <script>
                        class Car1 {
                            constructor(name, year) {
                                this.name = name;
                                this.year = year;
                            }
                            age(x) {
                                return x - this.year;
                            }
                        }
                        let date = new Date();
                        let year = date.getFullYear();
                        
                        let myCar = new Car1("Ford", 2014);
                        document.getElementById("demo2").innerHTML = "my car is " + myCar.age(year) + " years old.";
                    </script>
                

the syntax in classes must be written in "strict mode".

You will get an error if you do not follow the "strict mode" rules.

Examples

example: in a JavaScript class you cannot use variable without declaring it.

code:
                    <div class="spec">
                        <a id="demo3"></a>         
                    </div>
                    <script>
                        class Car2 {
                            constructor(name, year) {
                                this.name = name;
                                this.year = year;
            
                            }
                            age() {
                            //date1 = new Date();  // This will not work
                            let date1 = new Date(); // This will work
                            return date1.getFullYear() - this.year;
                            }
                        }
                        myCar = new Car2("Ford", 2013);
                        document.getElementById("demo3").innerHTML =  "My car is " + myCar.age() + " years old.";
                    </script>
                

The body of a class is the part that is in curly brackets {}.

top

This is where you define class members, such as methods or constructor. The body of a class is executed in strict mode even without the "use strict" directive.

A class element can be characterized by three aspects:

Kind: getter, setter, method, or field;
location: static or instance;
Visibility: public or private

Together, they add up to 16 possible combinations. To divide the reference more logically and avoid overlapping content, the different elements are introduced as follows:

Method definitions: public instance method;
getter: public instance getter;
setter: public instance setter;
Public class fields: public instance field;
static: public static method, getter, setter, and field;
Private class features: everything that's private.

Note: Private features have the restriction that all property names declared in the same class must be unique. All other public properties do not have this restriction — you can have multiple public properties with the same name, and the last one overwrites the others. This is the same behavior as in object initializers.

In addition, there are two special class element syntaxes: constructor and static initialization blocks, with their own references.

method definitions

Examples

code:
                    <div>
                        <p id="class1"></p>
                    </div>
                        <script>
                            class ClassWithPublicInstanceMethod {
                            publicMethod() {
                                return "hello world";
                            }
                            }
                            const instance = new ClassWithPublicInstanceMethod();
                            console.log(instance.publicMethod()); // "hello world"
                            document.getElementById("class1").innerHTML = instance.publicMethod();
                    </script>
                

Public instance methods are defined on the prototype property of the class and are thus shared by all instances of the class. They are writable, non-enumerable, and configurable.

Inside instance methods, this and super work like in normal methods. Usually, this refers to the instance itself.

In subclasses, super lets you access the prototype of the object that the method is attached to, allowing you to call methods from the superclass.

Examples

code:
                    <div>
                        <p id="class2"></p>
                    </div>
                    <script>
                        class BaseClass {
                            msg = "hello world";
                            basePublicMethod() {
                                return this.msg;
                            }
                        }
                        class SubClass extends BaseClass {
                            subPublicMethod() {
                                return super.basePublicMethod();
                            }
                        }
                        const instance2 = new SubClass();
                        console.log(instance2.subPublicMethod()); // "hello world"
                        document.getElementById("class2").innerHTML = instance2.subPublicMethod();
            
                    </script>
                    
                

getter

examples

code:
                    <div>
                        <p id="class3"></p>
                        <p id="class4"></p>
                    </div>
                    <script>
                        class ClassWithGetSet {
                            #msg = "hello world";
                            get msg() {
                                return this.#msg;
                            }
                            set msg(x) {
                                this.#msg = `hello ${x}`;
                            }
                        }
            
                        const instance3 = new ClassWithGetSet();
                        console.log(instance3.msg); // "hello world"
                        document.getElementById("class3").innerHTML = instance3. msg
                        instance3.msg = "cake";
                        console.log(instance3.msg); // "hello cake"      
                        document.getElementById("class4").innerHTML = instance3. msg    
                    </script>
                

Getter properties are defined on the prototype property of the class and are thus shared by all instances of the class. Unlike getter properties in object literals, getter properties in classes are not enumerable.

setter

Examples

code:
                    <div>
                        <p id="class5"></p>
                        <p id="class6"></p>
                    </div>
                    <script>
                        class ClassWithGetSet2 {
                            #msg = "hello world";
                            get msg() {
                                return this.#msg;
                            }
                            set msg(x) {
                                this.#msg = `hello ${x}`;
                            }
                        }
                        const instance4 = new ClassWithGetSet2();
                        console.log(instance4.msg); // "hello world"
                        document.getElementById("class5").innerHTML = instance4. msg
                        instance4.msg = "porto";
                        console.log(instance4.msg); // "hello cake"
                        document.getElementById("class6").innerHTML = instance4. msg
                    
                

Setter properties are defined on the prototype property of the class and are thus shared by all instances of the class. Unlike setter properties in object literals, setter properties in classes are not enumerable.

Public class fields

static

Examples

code:
                    <div>
                        <p id="class7"></p>
                        <p id="class8"></p>
                        <p id="class9"></p>
                        <p id="class10"></p>
                        <p id="class11"></p>
                        <p id="class12"></p>
                        <p id="class13"></p>
                        <p id="class14"></p>
                    </div>
                    <script>
                        class Triple {
                            static customName = "Tripler";
                            static description = "I triple any number you provide";
                            static calculate(n = 1) {
                                return n * 3;
                            }
                        }
                        class SquaredTriple extends Triple {
                            static longDescription;
                            static description = "I square the triple of any number you provide";
                            static calculate(n) {
                                return super.calculate(n) * super.calculate(n);
                            }
                        }
            
                        console.log(Triple.description); // 'I triple any number you provide'
                        document.getElementById("class7").innerHTML = Triple.description;
                        console.log(Triple.calculate()); // 3
                        document.getElementById("class8").innerHTML = Triple.calculate();
                        console.log(Triple.calculate(6)); // 18
                        document.getElementById("class9").innerHTML = Triple.calculate(6);
            
                        const tp = new Triple();
                        console.log(SquaredTriple.calculate(3)); // 81 (not affected by parent's instantiation)
                        document.getElementById("class10").innerHTML = SquaredTriple.calculate(3);
                        console.log(SquaredTriple.description); // 'I square the triple of any number you provide'
                        document.getElementById("class11").innerHTML = SquaredTriple.description;
                        console.log(SquaredTriple.longDescription); // undefined
                        document.getElementById("class12").innerHTML = SquaredTriple.longDescription;
                        console.log(SquaredTriple.customName); // 'Tripler'
                        document.getElementById("class13").innerHTML = SquaredTriple.customName;
                        // This throws because calculate() is a static member, not an instance member.
                        console.log(tp.calculate()); // 'tp.calculate is not a function'
                        document.getElementById("class14").innerHTML = tp.calculate();
                    
                

Private class features

Class fields are public by default, but private class members can be created by using a hash # prefix. The privacy encapsulation of these class features is enforced by JavaScript itself.

Private members are not native to the language before this syntax existed. In prototypical inheritance, its behavior may be emulated with WeakMap objects or closures, but they can't compare to the # syntax in terms of ergonomics.

Syntax:

        class ClassWithPrivate {
            #privateField;
            #privateFieldWithInitializer = 42;
        
            #privateMethod() {
                // …
            }
        
            static #privateStaticField;
            static #privateStaticFieldWithInitializer = 42;
        
            static #privateStaticMethod() {
                // …
            }
        }
    

There are some additional syntax restrictions:

All private identifiers declared within a class must be unique. The namespace is shared between static and instance properties. The only exception is when the two declarations define a getter-setter pair.
The private identifier cannot be #constructor.

Examples


code:
                    <div>
                        <p id="class15"></p><br>
                        <p id="class16"></p>
                    </div>
                    <script>
                        class ClassWithPrivateStaticField {
                            static #PRIVATE_STATIC_FIELD;
                            static publicStaticMethod() {
                                ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD = 42;
                                return ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD;
                            }
                            publicInstanceMethod() {
                                ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD = 42;
                                return ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD;
                            }
                        }
                        console.log(ClassWithPrivateStaticField.publicStaticMethod()); // 42
                        document.getElementById("class15").innerHTML = ClassWithPrivateStaticField.publicStaticMethod();
                        console.log(new ClassWithPrivateStaticField().publicInstanceMethod()); // 42      
                        document.getElementById("class16").innerHTML = new ClassWithPrivateStaticField.publicStaticMethod();
            
                    </script>
                

Class inheritance

top

to create a class inheritance, use the "extends" keyword

A class created with a class inheritance inherits all the methods from another class. Inheritance is useful for code reusability: reuse properties and methods of an existing class when you create a new class.

Examples

example: use the "extends" keyword to inherit all methods from another class.

use the "super" method to call the parent's constructor function.

code:
                    <div class="spec">
                        <p>use the <b>"super" method</b> to call the parent's constructor function.</p>
                        <a id="demo4"></a>
                    </div>
                    <script>
                        class Car3 {
                            constructor(brand) {
                                this.carname = brand;
                            }
                            present() {
                                return 'I have a ' + this.carname;
                            }
                        }
            
                        class Model extends Car3 {
                            constructor(brand, mod) {
                                super(brand);
                                this.model = mod;
                            }
                            show() {
                                return this.present() + ', it is a ' + this.model;
                            }
                        }
            
                        let myCar2 = new Model("Ford", "Mustang");
                        document.getElementById("demo4").innerHTML = myCar2.show();
                    </script>
                

classes allow to use getters and setters.

It can be smart to use "getters" and "setters" for your properties, especially if you want to do something special with the value before returning them, or before you set them. To add getters and setters in the class, use the "get" and "set" keywords.

Note: even if the getter is a method, you do not use parentheses when you want to get the property value.

Examples

example: a demonstration of how to add getters and setters in a class, and how to use the getter to get the property value.

code:
                    <div class="spec">
                        <a id="demo5"></a>         
                    </div>    
                    <script>
                        class Car4 {
                            constructor(brand) {
                                this.carname1 = brand;
                            }
                            get cnam() {
                                return this.carname1;
                            }
                            set cnam(x) {
                                this.carname1 = x;
                            }
                        }
                        let myCar3 = new Car4("Ford");
                        document.getElementById("demo5").innerHTML = myCar3.cnam;
                    </script>
                

The name of the getter/setter method cannot be the same as the name of the property , in this case carname. Many programmers use an underscore character ("_") before the property name to separate the getter/setter from the actual property.

hoisting : class declarations are not hoisted

That means that you must declare a class before you can use it.

Examples

example: you will get an error if you try to use a class before it is declared.

code:
                        <div class="spec">
                            <a id="demo6"></a>       
                        </div>
                        <script>
                                //You cannot use the class yet.
                                //myCar = new Car("Ford")
                                //This would raise an error.
                
                                class Car5 {
                                constructor(brand) {
                                    this.carname2 = brand;
                                }
                                }
                
                                //Now you can use the class:
                                let myCar4 = new Car5("Ford")
                                document.getElementById("demo6").innerHTML = myCar4.carname2;
                        </script>
                    

Static methods

top

Static class methods are defined on the class itself. You cannot call a "static" method on an object, only on an object class.

Examples

example: a static method is created with the "static" keyword, and you can only call the method on the class itself.

code:
                    <div class="spec">
                        <a id="demo7"></a>
                    </div>
                    <script>
                        class Car6 {
                            constructor(name) {
                                this.name = name;
                            }
                            static hello() {
                                return "Hello!!";
                            }
                        }
            
                        let myCar5 = new Car6("Ford");
            
                        //You can call 'hello()' on the Car Class:
                        document.getElementById("demo7").innerHTML = Car6.hello();
            
                        // But NOT on  a Car Object:
                        // document.getElementById("demo7A").innerHTML = myCar5.hello();
                        // this will raise an error.
                    </script>
                

Class examples

top

some examples

examples

the DOM

lorem error ipsum

lorem success ipsum

lorem success ipsum

lorem ipsum lorem

lorem ipsum success

error lorem ipsum

lorem ipsum lorem

lorem ipsum error

success lorem ipsum

code:
                <div>
                    <p class="par">lorem error ipsum</p>
                    <p class="par">lorem success ipsum</p>
                    <p class="par">lorem success ipsum</p>
                    <p class="par">lorem ipsum lorem</p>
                    <p class="par">lorem ipsum success</p>
                    <p class="par">error lorem ipsum</p>
                    <p class="par">lorem ipsum lorem</p>
                    <p class="par">lorem ipsum error</p>
                    <p class="par">success lorem ipsum</p>
                </div>
                <style>
                    .error {margin: 1rem; padding: 0.5rem; border: 3px dotted red; background: salmon; text-align: center; 
                        text-transform: uppercase;}
                    .success {margin: 1rem; padding: 0.5rem; border: 3px solid green; background: seagreen; color: #fff; 
                        text-transform: capitalize; text-align: center;}
                    .normal {margin: 1rem;  padding: 0.5rem; border: 3px solid #000; text-transform: capitalize; 
                        text-align: center;}
                </style>
                <script>
                    window.onload = function(){
                        const parag = document.querySelectorAll(".par");
                        const h4 = document.querySelector("h4");
                        
                        parag.forEach(par =>{
                            if (par.textContent.includes("error")) {
                                par.classList.add("error");
                            } else if (par.textContent.includes("success")) {
                                par.classList.add("success");
                            } else {
                                par.classList.add("normal");
                            }
                        });
                
                        h4.classList.toggle("test");
                    }; // window.onload
                </script>
            

Github - classes examples

examples
classes-constructor:
                class Polygon {
                    constructor() {
                    this.name = 'Polygon';
                    }
                }
                const poly1 = new Polygon();
                console.log(poly1.name);
                // expected output: "Polygon"
            
classes-extends:
                class DateFormatter extends Date {
                    getFormattedDate() {
                    const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                        'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
                    return `${this.getDate()}-${months[this.getMonth()]}-${this.getFullYear()}`;
                    }
                }
                console.log(new DateFormatter('August 19, 1975 23:15:30').getFormattedDate());
                // expected output: "19-Aug-1975"
                
            
classes-static
                class ClassWithStaticMethod {
                    static staticMethod() {
                    return 'static method has been called.';
                    }
                }
                console.log(ClassWithStaticMethod.staticMethod())
                // expected output: "static method has been called."
            

JavaScript.info - examples

examples
constructor function:
                    function User(name) {
                        this.name = name;
                        this.isAdmin = false;
                    }
                    let user = new User("Jack");
                    alert(user.name); // Jack
                    alert(user.isAdmin); // false
                
constructor mode test:new.target:
                    function User() {
                        alert(new.target);
                    }
                    // without "new":
                    User(); // undefined
                    // with "new":
                    new User(); // function User { ... }

                    function User(name) {
                        if (!new.target) { // if you run me without new
                            return new User(name); // ...I will add new for you
                        }
                        this.name = name;
                    }
                    let john = User("John"); // redirects call to new User
                    alert(john.name); // John
                
return from constructors:
                    function BigUser() {
                        this.name = "John";
                        return { name: "Godzilla" };  // <-- returns this object
                    }
                    alert( new BigUser().name );  // Godzilla, got that object

                    function SmallUser() {
                        this.name = "John";
                        return; // <-- returns this
                    }
                    alert( new SmallUser().name );  // John
                
methods in constructor:
                    function User(name) {
                        this.name = name;
                        this.sayHi = function() {
                        alert( "My name is: " + this.name );
                        };
                    }
                    let john = new User("John");
                    john.sayHi(); // My name is: John
                    /*
                    john = {
                        name: "John",
                        sayHi: function() { ... }
                    }
                    */