Class is useful for objects creation of the same kind. There is not 'real' classes in JS Classes in JS are based internally on constructor functions & prototypes Syntax class MyClass { prop = value constructor() {} // NO commas method1() {} method2() {} get something() {} set something(param) {} [Symbol.iterator]() {} } const obj = new MyClass() // create a new object with all the listed methods constructor() method is called when object is created with new keyword, so we can initialize the object there constructor() allows us to pass arguments into the class and assign it to an object MyClass is technically a constructor function methods, getters and setters are written to MyClass.prototype of a constructor function Create class User { constructor(name) { this.name = name } sayHi() { alert(this.name) } car = "bmw" dog = prompt("your dog breed?", "husky"); } let user = new User("John") // object is created user.sayHi() // "John" user.car // "bmw" user.dog // "husky" typeof User // function User.prototype // {constructor: ƒ, sayHi: ƒ} alert(User) // class User { ... } // string representation starts with the “class…” // for...in // does not work by default, coz enumerable flag is false for all methods in the "prototype" // Classes always "use strict" Class expression let User = class { sayHi() { alert("Hello") } } let User = class MyClass { sayHi() { alert("Hello") } } // Like a function, class expressions may have a name, it’s visible inside the class only let xxx = new User() xxx.sayHi() // Hello // return class in function on-demand function makeClass(phrase) { return class { sayHi() { alert(phrase) } } } let User = makeClass("Hello") // Create a new class new User().sayHi() // Hello Dynamic method name class User { ['say' + 'Hi']() { alert("Hello") } } new User().sayHi() // Hello this in class class Button { constructor(value) { this.value = value } click() { alert(this.value) } } let button = new Button("hello") setTimeout(button.click, 1000) // undefined // but works with arrow function, which takes "this" from above class Button { constructor(value) { this.value = value } click = () => alert(this.value) } let button = new Button("hello") setTimeout(button.click, 1000) // hello Methods chaining To enable methods chaining just return object's instance by returning this class Greetings { hi() { console.log('hi'); return this; } bye() { console.log('bye'); return this; } } const greeting = new Greetings() greeting.hi().bye() // hi // bye Extends class Animal { constructor(name) { this.speed = 0 this.name = name } setSpeed(speed) { this.speed = speed } } let dog = new Animal("Spok") dog.name // 'Spok' dog.setSpeed(30) dog.speed // 30 dog.doFly() // !!! dog.doFly is not a function class Bird extends Animal { doFly() { return true } } let owl = new Bird('Lintu') owl.name // 'Lintu' owl.setSpeed(100) owl.speed // 100 owl.doFly() // true Override method class Animal { constructor(name) { this.speed = 0 this.name = name } setSpeed(speed) { this.speed = speed } } let dog = new Animal("Spok") dog.name // 'Spok' dog.setSpeed(30) dog.speed // 30 class Bird extends Animal { setSpeed(speed) { this.speed = 2 * speed } } let owl = new Bird('Lintu') owl.name // 'Lintu' owl.setSpeed(100) owl.speed // 200 Override constructor constructors in inheriting classes must call super before using this super calls parent method arrow functions do not have super class Animal { constructor(name) { this.speed = 0 this.name = name } } let rabbit = new Animal ("White Rabbit", 10) // {speed: 0, name: "White Rabbit"} class Rabbit extends Animal { constructor(name, earLength) { super(name) this.earLength = earLength } } rabbit = new Rabbit("White Rabbit", 10) // {speed: 0, name: "White Rabbit", earLength: 10} Static methods & properties can be accessed directly from the class without object instantiation (no idea why it is useful) they belong to the class they are not part of an instantiated object static properties and methods are inherited Static methods class Man { name = "John" static hi() { return 'John says hi' } } // same as class Man { name = "John" } Man.hi = function() { return 'John says hi' } const john = new Man john.name // 'John' john.hi() // ! Uncaught TypeError: john.hi is not a function Man.hi() // 'John says hi' Static property class Article { static publisher = "John" } // or Article.publisher = "John" const news1 = new Article news1.publisher // undefined Article.publisher // 'John' Read - only property property can be set & never modified to do that need to make getter, but not the setter class CoffeeMachine { constructor(power) { this._power = power } get power() { return this._power } } let coffeeMachine = new CoffeeMachine(100) alert(`Power is: ${coffeeMachine.power} W`) // Power is: 100 W coffeeMachine.power = 25; // Error (no setter) Private properties & methods only accessible from inside the class not supported widely yet should start with # inherits have no direct access class CoffeeMachine { #waterAmount = 666 waterAmount() { return this.#waterAmount } } let machine = new CoffeeMachine() machine.waterAmount() // 666 machine.#waterAmount // Error Private field '#waterAmount' must be declared in an enclosing class // can not be inherited class MegaCoffeeMachine extends CoffeeMachine { method() { alert( this.#waterAmount ); // Error Private field '#waterAmount' must be declared in an enclosing class } } Getters & setters Getters & setters can modify property values when we write or read them from the object. class Human { get name() { return this._name } set name(str) { const letters = [...str] const [firstLetter, ...otherLetters] = letters this._name = [firstLetter.toUpperCase(), ...otherLetters].join('') } } const john = new Human john.name = 'john' john.name // 'John' class CoffeeMachine { #waterAmount = 0 get waterAmount() { return this.#waterAmount } set waterAmount(value) { if (value < 0) value = 0 this.#waterAmount = value } } let machine = new CoffeeMachine() machine.waterAmount = -666 machine.waterAmount // 0 machine.waterAmount = 100 machine.waterAmount // 100 Getters & setters via functions can accept multiple arguments more flexible class CoffeeMachine { #waterAmount = 0 setWaterAmount(value) { if (value < 0) value = 0 this.#waterAmount = value } getWaterAmount() { return this.#waterAmount } } const machine = new CoffeeMachine() machine.setWaterAmount(100) machine.getWaterAmount() // 100 Extend built -in classes Built-in classes like Array, Map, Object and others are extendable Can add additional methods to it Static methods are not inherited class MyArray extends Array { isEmpty() { return this.length === 0 } } let arr = new MyArray(1, 2, 5, 10, 50) arr // MyArray(5) [1, 2, 5, 10, 50] arr.constructor === MyArray // true let filteredArr = arr.filter(item => item >= 10) filteredArr // MyArray(2) [10, 50] filteredArr.constructor === MyArray // true filteredArr.isEmpty() // false filteredArr.length = 0 filteredArr // MyArray [] filteredArr.isEmpty() // true [Symbol.species] If we’d like built-in methods like map or filter to return regular arrays return Array in [Symbol.species] class MyArray extends Array { isEmpty() { return this.length === 0 } static get [Symbol.species]() { return Array } } let arr = new MyArray(1, 2, 5, 10, 50) arr // MyArray(5) [1, 2, 5, 10, 50] arr.isEmpty() // false let filteredArr = arr.filter(item => item >= 10); filteredArr // [10, 50] filteredArr.isEmpty() // TypeError: filteredArr.isEmpty is not a function instanceof Checks if an object belongs to a certain class Returns true if object belongs to the Class or inherits from it Examines the prototype chain for the check Same as objA.isPrototypeOf(objB) class Rabbit {} let rabbit = new Rabbit() rabbit instanceof Rabbit // true function Dog() {} new Dog() instanceof Dog // true let arr = [1, 2, 3]; arr instanceof Array // true arr instanceof Object // true Mixins object can inherit from a single object class may extend only one other class Sometimes it may be limiting Mixin is a class with methods that can be used by other classes without a need to inherit from it Mixin is a class that contains methods for other classes let sayHiMixin = { sayHi() { alert(`Hello ${this.name}`) }, sayBye() { alert(`Bye ${this.name}`) } }; class User { constructor(name) { this.name = name; } } // copy the methods Object.assign(User.prototype, sayHiMixin); // now User can say hi new User("Dude").sayHi(); // Hello Dude! Good but difficult example on real mixin usage. Class vs constructor function Example is taken from the OOP lesson , which shows how classes are internally done in JavaScript by prototyping. Class version let usersArr = [] class User { constructor(email, name) { this.email = email this.name = name this.online = false usersArr.push(this) } login() { this.online = true console.log(this.email, 'has logged in') } logout() { this.online = false console.log(this.email, 'has logged out') } } class Admin extends User { constructor(email, name) { super(email, name) this.role = '' } deleteUser(u) { usersArr = usersArr.filter(user => user.email !== u.email) console.log(usersArr) } } const userOne = new User('john@mail.com', 'John') const userTwo = new User('bob@mail.com', 'Bob') const admin = new Admin('mike@mail.com', 'Mike') usersArr // [User, User, Admin] admin.deleteUser(userOne) usersArr // [User, Admin] Constructor function version let usersArr = [] function User(email, name) { this.email = email this.name = name this.online = false usersArr.push(this) } User.prototype.login = function() { this.online = true console.log(this.email, 'has logged in') } User.prototype.logout = function() { this.online = false console.log(this.email, 'has logged out') } function Admin(...args) { User.apply(this, args) this.role = '' } Admin.prototype = Object.create(User.prototype) Admin.prototype.deleteUser = function(u) { usersArr = usersArr.filter(user => user.email !== u.email) } const userOne = new User('john@mail.com', 'John') const userTwo = new User('bob@mail.com', 'Bob') const admin = new Admin('mike@mail.com', 'Mike') usersArr // [User, User, Admin] admin.deleteUser(userOne) usersArr // [User, Admin]