betacode

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

Следуйте за нами на нашей фан-странице, чтобы получать уведомления каждый раз, когда появляются новые статьи. Facebook

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

ECMAScript 5 не имеет явного понятия класса (Class), вместо этого он симулирует класс основываясь на 2 принципах это function & prototype. Так как ES5 был представлен в 2011 году, на данный момент его синтаксис широко распространен, но мы не можем отрицать то, что создание класса таким способом сложно понять программистам. При этом, такие языки как Java, C#,.. имеют современный способ для создания класса. Версия ES6 вышла в 2015 году и вовремя закрывает эту проблему, она дает современный синтаксис для определения класса.
С новым синтаксисом ECMAScript 6 уменьшил сложность для программиста при создании класса, так же уменьшил сложность создания подкласса (Subclass). Но технически ничего не изменилось по сравнению с предыдущей версией. 
Ниже у меня есть прямоугольник (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 );
Запуск примера:

Height 5
Width 3
Area 15
Что произойдет когда вы создадите объект из 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
Output:

Height 5
Width 3
Invalid width -100
Height 5
Width 3
Height 5
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);
Output:

Distance between 2 points: 14.142135623730951

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
Запуск примера:

r1 === r2 ? false
r1 === r3 ? true
r1 !== r2 ? true
r1 !== r3 ? false

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

Maybe you are interested

Это онлайн курс вне вебсайта o7planning, который мы представляем, он включает бесплатные курсы или курсы со скидкой.