[[Инженер JavaScript — 7]]
- ООП-функции строго типизированного класса
- JavaScript не поддерживает пользовательские типы
- назначение класса ES6
- Сравнение класса ES6 с функциями конструктора
- поля экземпляра, назначенные на уровне класса, перемещаются внутрь конструктора
- Функции стрелок, назначенные на уровне класса
- функция this в стрелке на уровне класса в основном является замыканием над this в конструкторе
- Единое наследование с классом ES6
В ООП класс используется для определения новых типов данных. Эти типы данных следуют строгой иерархии и поддерживают переопределение, полиморфизм, абстракцию, инкапсуляцию и т. д. Я настоятельно рекомендую прочитать статью о функциях-конструкторах, прежде чем изучать класс.
Добавление нового типа невозможно в 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]], функций, области действия и замыкания и т. д. при совместном использовании может быть действительно сложной и мощной одновременно. Понимание этих идей крайне необходимо для понимания класса.
Статические поля внутри класса фактически добавляются как свойство функции-конструктора, а не в создаваемый объект.