Классы и объекты в ECMAScript

1- Ваш первый Класс

ECMAScript 5 не имеет явного понятия класса (Class), вместо этого он симулирует класс основываясь на 2 принципах это  function & prototype. Так как  ES5 был представлен в 2011 году, на данный момент его синтаксис широко распространен, но мы не можем отрицать то, что создание класса таким способом сложно понять программистам. При этом, такие языки как Java, C#,.. имеют современный способ для создания класса. Версия  ES6 вышла в 2015 году и вовремя закрывает эту проблему, она дает современный синтаксис для определения класса.
  • TODO Link!
Ниже у меня есть прямоугольник ( Rectangle), он имеет 2 важных свойства  ( property) это  width (ширина) и  height (высота). Мы определим класс с названием  Rectangle для ее симуляцииđể с синтаксисом  ES6.
Создать исходный файл  rectangle.js:
rectangle.js
// Define a class.
class Rectangle  {

    // Constructor имеет 2 параметра.
    // (Используется для создания объекта)
    // this.width указывает на property (свойство) width класса.
    constructor (width = 5 , height = 10)  {
        this.width = width;
        this.height = height;
    }

    // Метод используется для расчета периметра прямоугольника.
    getArea() {
        var area = this.width * this.height
        return area
    }

}

// Создать 1 объект класса Rectangle через Constructor.
var rect = new Rectangle(3, 5);

console.log("Height "+ rect.height);
console.log("Width "+ rect.width);

// Вызвать метод
let area = rect.getArea();
console.log("Area "+ area );
Запуск примера:
Что произойдет когда вы создадите объект из constructor класса?
Когда вы вызываете  Constructor класса, создается новый объект, и к  property объекта будут прикреплены значения из параметров.
В ECMAScript каждый класс имеет максимум один constructor. Подобно функции, параметры  constructor так же могут имет значение по умолчанию. Поэтому вы можете создать объекты разными способами.
// width = 3, height = 5
let rect = new Rectangle(3, 5);

// width = 15, height = 10 (Default)
let rect2 = new Rectangle(15);

// width = 5 (Default), height = 50
let rect3 = new Rectangle(undefined, 50);


// width = 5 (Default), height = 10 (Default)
let rect4 = new Rectangle();
 

2- Getter & Setter

Перед тем как дать понятие Getter & Setter, проанализируем ситуацию:
Предположим у нас есть класс  Person, данный класс имеет property это  name.
Class Person
class Person  {

    constructor(name) {
      this.name = name;
    }
}
И вы можете получить доступ в  property объекта, или прикрепить новые значения для него и не встретить какие-либо пробемы.
// Create an object
let person = new Person("Tom");

// Access to property name.
console.log( person.name); // Tom

// Assign new value to property name.
person.name = "Jerry"; // !!!

console.log( person.name ); // Jerry
Свободный доступ в  property и изменение его значения вне класса на самом деле опасно. Иногда вы хотите иметь property к которому нет доступа снаружи, или вне класса нельзя прикрепить к нему новые значения. Getter/Setter позволяет вам создать такой property.
Ключевое слово  get ставится перед методом класса без параметра помогает создать property с названием, которое является названием метода. Данный метод вызывается каждый раз когда программа заходит в данный  property.
getter-example1.js
class Person  {

    constructor (name)  {
       // property: __name
       this.__name = name;
    }

    // Getter of property name
    get name()  {
       console.log("Call getter of property 'name'!!");
       return this.__name;
    }

}

// ------------ TEST -----------------

let person = new Person("Tom");

// Access to property 'name' ==> Call getter
console.log( person.name); // Tom

// Assign new value to property name.
person.name = "Jerry"; // Not Working!!!!

// Access to property 'name' ==> Call getter
console.log( person.name); // Tom
 
Ключевое слово  set ставится перед методом класса с одним параметром, помогает создать   property с названием, которое является названием метода. Данный метод будет вызван каждый раз, когда программа прикрепляет новое значение данному  property.
setter-example1.js
class Person  {

    constructor (name)  {
       // property: __name
       this.__name = name;
    }

    // Setter of property name
    set name(newName)  {
       console.log("Call setter of property 'name'!!");
       this.__name = newName;
    }

    // A method
    showInfo()  {
       console.log("Person: " + this.__name);
    }
}

// ------------ TEST -----------------

let person = new Person("Tom");

// Can not access to property 'name'
console.log( person.name); // undefined

// Set new value to property 'name' ==> Call setter
person.name = "Jerry";

person.showInfo(); // Person: Tom
Например  property с обоими  Getter & Setter:
getter-setter-example.js
class Rectangle  {

    constructor (width = 5 , height = 10)  {
        this.__width = width;
        this.height = height;
    }

    // Getter of property 'width'
    get width()  {
      return this.__width;
    }

    // Setter of property 'width'
    set width(newWidth)  {
      if(newWidth > 0) {
          this.__width = newWidth;
      } else {
          console.log("Invalid width " + newWidth);
      }
    }

}

// ------------ TEST ------------------


var rect = new Rectangle(3, 5);

console.log("Height "+ rect.height); // Height: 5
console.log("Width "+ rect.width); // Width: 3

rect.width = -100;

console.log("Height "+ rect.height); // Height: 5
console.log("Width "+ rect.width); // Width: 3


rect.width = 100;

console.log("Height "+ rect.height); // Height: 5
console.log("Width "+ rect.width); // Width: 100
Свойства ( property) с приставками 2 нижние черты ( __ ) условлены программистами не использовать вне класса. Но это соглашение может быть кем-то нарушено. Поэтому использовать property таким образом опасно.
Если вы хотите иметь  property по настоящему индивидуальным (Private), стоит его назвать с приставкой хэштега (hashtag) ( # ). Но данный код может быть запущен только с помощью  Babel 7 или новее.
Private property
class Something {

  constructor(){

    this.#someProperty = "test";
  }

}

const instance = new Something();

console.log(instance.#someProperty); // undefined
getter-example2.js
class Person  {

    constructor (name)  {
       // Private property: #name
       this.#name = name;
    }

    // Getter of property name
    get name()  {
       console.log("Call getter of property 'name'!!");
       return this.#name;
    }

}

3- Статическое поле (Static Field)

Ключевое слово  static появляется в объявлении  Getter или  Setter  помогает вам определить статическое поле ( static field). Вы можете получить доступ в  static field через названия класса.
Статические поля ( static field) имеют фиксированное значение (Неизменное) называется константное статическое поле ( constant static field).
static-field-example1.js
class Employee {

   constructor (fullName, age)  {
     this.fullName = fullName;
     if(age < Employee.MIN_AGE || age > Employee.MAX_AGE)  {
        throw "Invalid Age " + age;
     }
     this.age = age;
   }

   // A static field: MIN_AGE
   static get MIN_AGE() {
      return 18;
   }

   // A static field: MAX_AGE
   static get MAX_AGE() {
     if(!Employee.__MAXA)  {
        Employee.__MAXA = 60;
     }
     return Employee.__MAXA;
   }

   static set MAX_AGE(newMaxAge)  {
      Employee.__MAXA = newMaxAge;
   }

}

// ---- TEST ---------

console.log("Mininum Age Allowed: " + Employee.MIN_AGE);
console.log("Maximum Age Allowed: " + Employee.MAX_AGE);

// Set new Maximum Age:
Employee.MAX_AGE = 65;

console.log("Maximum Age Allowed: " + Employee.MAX_AGE);

let baby = new Employee("Some Baby", 1); // Error!!
 
Вы имеете другой способ объявления статического поля для класса, но статические поля созданные таким способом не будут константой (Constant) так как его значение может поменяться. Ниже дается пример:
static-field-example2.js
class Employee {

   constructor (fullName, age)  {
     this.fullName = fullName;
     if(age < Employee.MIN_AGE || age > Employee.MAX_AGE)  {
        throw "Invalid Age " + age;
     }
     this.age = age;
   }
}

Employee.MIN_AGE = 18;
Employee.MAX_AGE = 60;

// ---- TEST ---------

console.log("Mininum Age Allowed: " + Employee.MIN_AGE);
console.log("Maximum Age Allowed: " + Employee.MAX_AGE);

// Set new Maximum Age:
Employee.MAX_AGE = 65;

console.log("Maximum Age Allowed: " + Employee.MAX_AGE);

let baby = new Employee("Some Baby", 1); // Error!!

4- Статический метод

Ключевое слово  static появляется в объявлении метода класса, помогает вам определить статический метод ( static method). Вы можете вызвать статический метод через название класса. Статический метод не может быть вызван через объект класса. Статический метод обычно используется как утилитарная функция приложения.
point.js
class Point {

  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  // Расчитать расстояние между 2 пунктами
  static distance( point1, point2) {
    const dx = point1.x - point2.x;
    const dy = point1.y - point2.y;

    return Math.hypot(dx, dy);
  }
}

// --- TEST ---
let point1 = new Point( 5, 10);
let point2 = new Point( 15, 20);

// Distance
let d = Point.distance(point1, point2);

console.log("Distance between 2 points: " + d);

5- Оператор сравнивания объектов

В  ECMAScript, когда вы создаете объект через конструктор (constructor) реальный объект создается в памяти, и имеет определенный адрес. 

Оператор назначает объект  AA объектом  BB не создавая новые объекты в памяти, это просто указывающий адрес от  AA до адреса  BB.
Оператор === используется для сравнения 2  указывающих объектов, возвращает  true если 2 объекта указывают на одинаковый адрес в памяти. Оператор !== используется для сравнения 2 адресов 2 указывающих объектов, возвращает true если 2 объекта указывают на 2 разных адреса. 
identify-operator-example.js
// Define a class.
class Rectangle  {
    constructor (width = 5 , height = 10)  {
        this.width = width;
        this.height = height;
    }
    getArea() {
        var area = this.width * this.height;
        return area;
    }
}

// Create object: r1
let r1 = new Rectangle( 20,  10);

// Create object: r2
let r2 = new Rectangle( 20, 10);

let r3 = r1;


let  b12  =   r1 === r2; // false
let  b13  =   r1 === r3; // true

console.log("r1 === r2 ? " + b12); // false
console.log("r1 === r3 ? " + b13); // true


var bb12  = r1 !== r2; // true
var bb13  = r1 !== r3; // false


console.log("r1 !== r2 ? " + bb12); // true
console.log("r1 !== r3 ? " + bb13); // false
Запуск примера:

6- Наследствие и полиморфизм