[[Инженер JavaScript — 7]]

В ООП класс используется для определения новых типов данных. Эти типы данных следуют строгой иерархии и поддерживают переопределение, полиморфизм, абстракцию, инкапсуляцию и т. д. Я настоятельно рекомендую прочитать статью о функциях-конструкторах, прежде чем изучать класс.

Добавление нового типа невозможно в JavaScript. Так чем же тогда полезен класс в JavaScript?

Класс в JavaScript — это другой способ создания объектов, которые могут содержать данные и отображать поведение с помощью цепочки [[Prototype]]. Таким образом, он был представлен в JavaScript с идеей упрощения синтаксиса функции-конструктора. Класс — это просто функция с большим количеством синтаксиса. ES2022 вводит множество сложных функций в классы, и они больше не отличаются синтаксисом от функций-конструкторов, а используются для достижения более сложных целей. Мы обсудим их в отдельной статье.

class A {
    // regular function
    constructor(val) {
        this.x = val;
    }
    
    showX() {
        console.log(this.x);
    }
}

console.log(typeof A);

console.log(A.prototype);
console.log(A.prototype.showX);

const obj = new A(200); // this will be an empty object whose [[Prototype]] is linked to A.prototype
console.log(obj);
console.log(typeof obj);
console.log(Object.getPrototypeOf(obj) === A.prototype);

const otherObj = new A(600);
console.log(otherObj);

Итак, как мы изобразим это, как показано ниже.
класс A — это функция типа «функция»
object obj — это объект типа «объект».
obj.[[Prototype]] === A.prototype
Все обычные функции, написанные с использованием синтаксиса метода класса внутри тела класса, входят в A.prototype

showX — это просто обычная функция, добавленная как свойство дескриптора данных в A.prototype.

Таким образом, класс может помочь объекту иметь данные и выполнять поведение.

class A {
  x = 20;
  y = 50;
  
  constructor(zVal) {
    this.z = zVal;
  }

  showValues() {
    console.log(this.x);
    console.log(this.y);
    console.log(this.z);
  }
}

const obj = new A(200);
const otherObj = new A(400);
console.log(obj);
console.log(otherObj);

Обратите внимание, что свойства x, y добавляются к объекту, создаваемому конструктором. Это эквивалентно приведенному ниже коду

class A {

  constructor(zVal) {  
      this.x = 20;
      this.y = 50;
     // Added properties before the rest of the constructor definition executes

      this.z = zVal;
  }

  showValues() {
    console.log(this.x);
    console.log(this.y);
    console.log(this.z);
  }
}

const obj = new A(200);
const otherObj = new A(400);
console.log(obj);
console.log(otherObj);

Итак, что произойдет, если у нас есть стрелочная функция в классе

class A {
  x = 20; // Note the assignment operation
  y = 50; // Note the assignment operation
  
  constructor(zVal) {
    this.z = zVal;
  }

  showValues() {
    console.log(this.x);
    console.log(this.y);
    console.log(this.z);
  }

  // Note the assignment operation
   showZOnly = () => {
      console.log(this.z);
  }
}

const obj = new A(200);
console.log(obj);
const zShower = obj.showZOnly;
const allShower = obj.showValues;
zShower(); // this still points to obj, 
allShower(); // implicit lost default binding,

Он будет похож на приведенный ниже код и, следовательно, будет добавлен в качестве свойства данных в новый объект, предполагая, что [[Set]] будет определять свойство.

class A {
  
  constructor(zVal) {
    this.x = 20;
    this.y = 50; // this at the time constructor executes here
    
    Object.defineProperty(this, 'showZOnly',  {
     enumerable: true,
     writable: true,
     configurable: true,

     value: () => {
        // will be hard bound through closure inside the arrow function here
        console.log(this.z);  
    };
    
    this.z = zVal;
  }

  showValues() {
    console.log(this.x);
    console.log(this.y);
    console.log(this.z);
  }
 
}

const obj = new A(200);
console.log(obj);
const zShower = obj.showZOnly;
const allShower = obj.showValues;
zShower(); // arrow function this, still points to obj, 
allShower(); // regular function this, implicit lost default binding,

Напомним, что this для стрелочной функции на самом деле просто хранится в замыкании для стрелочной функции и используется вместо этого ключевого слова.

Единое наследование с классом ES6:

В ООП класс может наследоваться от другого класса, в JavaScript нам нужно использовать цепочку [[Prototype]] таким образом, чтобы имитировать ее идею. Мы делаем это так же, как мы делаем это с функцией-конструктором.

class Point2D {
    constructor(a, b) {
        this.a = a;
        this.b = b;
    }
    
    showPoint() {
        console.log("this.a ", this.a);
        console.log("this.b ", this.b);
    }
}

class Point3D extends Point2D {
    constructor(a, b, c) {
        super(a, b);
        this.c = c;
    }
    
    showPoint() {
        super.showPoint();
        console.log("this.c ", this.c);
    }
    
    changeXCoOrdinate(a) {
        this.a = a;
    }
    changeYCoOrdinate(b) {
        this.b = b;
    }
    changeZCoOrdinate(c) {
        this.c = c;
    }
    
}

const myFavoritePoint = new Point3D(10, 20, 30);
const yourFavoritePoint = new Point3D(50, 100, 200); // line (*)

console.log("my fav point ");
myFavoritePoint.showPoint();
console.log("you fav point ");
yourFavoritePoint.showPoint();
console.log("changing my fav point x coordinate ");
myFavoritePoint.changeXCoOrdinate(12);
console.log("showing my fav point after changing x coordinate ");
myFavoritePoint.showPoint();
console.log("change z coordinate of your fav point");
yourFavoritePoint.changeZCoOrdinate(45);
console.log("showing your fav point after changing z coordinate ");
yourFavoritePoint.showPoint();

Цепочка [[Prototype]] после строки (*) показана выше.

Идея этой привязки, объектов, цепочки [[Prototype]], механизма [[Get]], механизма [[Set]], функций, области действия и замыкания и т. д. при совместном использовании может быть действительно сложной и мощной одновременно. Понимание этих идей крайне необходимо для понимания класса.

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