Классы javascript: Класс: базовый синтаксис

Содержание

Классы — JavaScript | MDN

Классы в JavaScript были введены в ECMAScript 2015 и представляют собой синтаксический сахар над существующим в JavaScript механизмом прототипного наследования. Синтаксис классов не вводит новую объектно-ориентированную модель, а предоставляет более простой и понятный способ создания объектов и организации наследования.

Объявление класса

Первый способ определения класса — class declaration (объявление класса). Для этого необходимо воспользоваться ключевым словом class и указать имя класса (в примере — «Rectangle»).

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}
Подъём (hoisting)

Разница между объявлением функции (function declaration) и объявлением класса (class declaration) в том, что объявление функции совершает подъём (hoisted), в то время как объявление класса

 — нет. Поэтому вначале необходимо объявить ваш класс и только затем работать с ним, а код же вроде следующего сгенерирует исключение типа ReferenceError:

var p = new Rectangle(); 

class Rectangle {}

Выражение класса

Второй способ определения класса — class expression (выражение класса). Можно создавать именованные и безымянные выражения. В первом случае имя выражения класса находится в локальной области видимости класса и может быть получено через свойства самого класса, а не его экземпляра.


var Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);



var Rectangle = class Rectangle2 {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);

Обратите внимание: выражения класса подвержены тем же проблемам с подъёмом (hoisting), что и

объявления класса.

Тело класса — это часть кода, заключённая в фигурные скобки {}. Здесь вы можете объявлять члены класса, такие как методы и конструктор.

Строгий режим

Тела объявлений классов и выражений классов выполняются в строгом режиме (strict mode).

Constructor

Метод constructor — специальный метод, необходимый для создания и инициализации объектов, созданных, с помощью класса. В классе может быть только один метод с именем constructor. Исключение типа SyntaxError будет выброшено, если класс содержит более одного вхождения метода constructor.

Ключевое слово super можно использовать в методе constructor для вызова конструктора родительского класса.

Методы прототипа

Смотрите также определение методов.

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }

  get area() {
    return this.calcArea();
  }

  calcArea() {
    return this.height * this.width;
  }
}

const square = new Rectangle(10, 10);

console.log(square.area); 

Статические методы  и свойства

Ключевое слово static, определяет статический метод или свойства для класса. Статические методы и свойства вызываются без инстанцирования (en-US) их класса, и не могут быть вызваны у экземпляров (instance) класса. Статические методы, часто используются для создания служебных функций для приложения, в то время как статические свойства полезны для кеширования в рамках класса, фиксированной конфигурации или любых других целей, не связанных с реплецированием данных между экземплярами.

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

  static displayName = "Точка";
  static distance(a, b) {
    const dx = a.x - b.x;
    const dy = a.y - b.y;

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

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
p1.displayName; 
p1.distance;    
p2.displayName; 
p2.distance;    

console.log(Point.displayName);      
console.log(Point.distance(p1, p2)); 

Привязка 

this в прототипных и статических методах

Когда статический или прототипный метод вызывается без привязки к this объекта (или когда this является типом boolean, string, number, undefined, null), тогда this будет иметь значение undefined внутри вызываемой функции. Автоупаковка не будет произведена. Поведение будет таким же как если бы мы писали код в нестрогом режиме.

class Animal {
  speak() {
    return this;
  }
  static eat() {
    return this;
  }
}

let obj = new Animal();
obj.speak(); 
let speak = obj.speak;
speak(); 

Animal.eat() 
let eat = Animal.eat;
eat(); 

Если мы напишем этот же код используя классы основанные на функциях, тогда произойдёт автоупаковка основанная на значении this, в течение которого функция была вызвана. В строгом режиме автоупаковка не произойдёт — значение this останется прежним.

function Animal() { }

Animal.prototype.speak = function(){
  return this;
}

Animal.eat = function() {
  return this;
}

let obj = new Animal();
let speak = obj.speak;
speak(); 

let eat = Animal.eat;
eat(); 

Свойства экземпляра

Свойства экземпляра должны быть определены в методе класса:

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

Статические (class-side) свойства и свойства прототипа должны быть определены за рамками тела класса:

Rectangle.staticWidth = 20;
Rectangle.prototype.prototypeWidth = 25;

Определение полей

Публичные и приватные поля — это экспериментальная особенность (stage 3), предложенная комитетом TC39 по стандартам языка Javascript. Поддержка браузерами ограничена, но это нововведение может быть использовано на моменте сборки, используя к примеру Babel.

Публичные поля

Используя Javascript синтаксис определения полей, приведённый выше пример может быть изменён следующим образом:

class Rectangle {
  height = 0;
  width;
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

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

Более подробно об этом написано в публичные поля класса.

Приватные поля

Предыдущий пример может быть изменён следующим образом, используя приватные поля:

class Rectangle {
  #height = 0;
  #width;
  constructor(height, width) {
    this.#height = height;
    this.#width = width;
  }
}

Приватные поля могут быть изменены или прочитаны только в рамках класса и не могут быть вызваны извне. Определяя вещи, которые не видны за пределами класса, вы гарантируете, что пользователи ваших классов не могут зависеть от внутренних компонентов, которые могут изменить версию на версию.

Приватные поля могут быть объявлены только заранее в объявлении поля.

Приватные поля не могут быть созданы позже путём присваивания им значения, в отличии от обычных свойств.

Более подробно об этом написано в Приватные поля класса.

Ключевое слово extends используется в объявлениях классов и выражениях классов для создания класса, дочернего относительно другого класса.

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} издаёт звук.`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); 
  }

  speak() {
    console.log(`${this.name} лает.`);
  }
}

let d = new Dog('Митци');
d.speak(); 

Если в подклассе присутствует конструктор, он должен сначала вызвать super, прежде чем использовать this.

Аналогичным образом можно расширять традиционные, основанные на функциях «классы»:

function Animal (name) {
  this.name = name;
}
Animal.prototype.speak = function () {
  console.log(`${this.name} издаёт звук.`);
}

class Dog extends Animal {
  speak() {
    console.log(`${this.name} лает.`);
  }
}

let d = new Dog('Митци');
d.speak(); 

Обратите внимание, что классы не могут расширять обычные (non-constructible) объекты. Если вам необходимо создать наследование от обычного объекта, в качестве замены можно использовать Object.setPrototypeOf():

var Animal = {
  speak() {
    console.log(`${this.name} издаёт звук.`);
  }
};

class Dog {
  constructor(name) {
    this.name = name;
  }
}


Object.setPrototypeOf(Dog.prototype, Animal);

let d = new Dog('Митци');
d.speak(); 

Допустим, вам хотелось бы возвращать объекты типа Array в вашем производном от массива классе MyArray. Паттерн species позволяет вам переопределять конструкторы по умолчанию.

Например, при использовании таких методов, как map(), который возвращает конструктор по умолчанию, вам хотелось бы, чтобы они возвращали родительский объект Array вместо объекта MyArray. Символ Symbol.species позволяет это реализовать:

class MyArray extends Array {
  
  static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);

console.log(mapped instanceof MyArray); 
console.log(mapped instanceof Array);   

Ключевое слово super используется для вызова функций на родителе объекта.

class Cat {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} издаёт звук.`);
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(`${this.name} рычит.`);
  }
}

let l = new Lion('Фаззи');
l.speak();


Абстрактные подклассы, или mix-ins, — это шаблоны для классов. У класса в ECMAScript может быть только один родительский класс, поэтому множественное наследование (к примеру, от tooling classes) невозможно. Функциональность должен предоставлять родительский класс.

Для реализации mix-ins в ECMAScript можно использовать функцию, которая в качестве аргумента принимает родительский класс, а возвращает подкласс, его расширяющий:

var calculatorMixin = Base => class extends Base {
  calc() { }
};

var randomizerMixin = Base => class extends Base {
  randomize() { }
};

Класс, использующий такие mix-ins, можно описать следующим образом:

class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }

BCD tables only load in the browser

Класс не может быть переопределён. Попытка этого приведёт к SyntaxError .

Если мы запускаете код в веб браузере, к примеру в Firefox Web Console (Tools > Web Developer > Web Console) и вы используете (‘Run’) определение класса с одним и тем же именем дважды, вы получите SyntaxError: redeclaration of let ClassName;. (Обсуждение по ошибке можно посмотреть в баг 1428672.) Chrome Developer Tools возвращает сообщение типа 

Uncaught SyntaxError: Identifier 'ClassName' has already been declared at <anonymous>:1:1.

Как работают классы в JavaScript. Вдохновение How JavaScript Classes Work… | by Hydrock | Front Stories

Вдохновение How JavaScript Classes Work от Thon Ly

Введение

Введение классов (Classes) в JavaScript должно было сделать создание объектов более очевидным. Но на самом деле, классы в js — это всего лишь синтаксический сахар, и это может ввести в заблуждение, особенно людей из более «традиционного» программирования. Под капотом, классы в JS являются объектами. Классов в «классическом» смысле не существует.

Например, класс JavaScript для создания экземпляра автомобиля теперь имеет вот такой простой синтаксис:

После создания, объект будет иметь свойства maker, model и метод drive.

Чтобы создать класс Tesla, наследуемый от класса Car, делаем так:

К сожалению, такая простота может скрыть истинное понимание языка. Чтобы понять, что на самом деле происходит, мы попытаемся воссоздать класс Tesla, который наследуется от класса Car, используя только ванильный JavaScript.

Конструктор используется для установки свойств объекта в момент создания экземпляра. Хотя это и похоже на другие языки, в JavaScript это просто функция.

Когда мы вызываем функцию с ключевым оператором new, создается новый, пустой объект. В данном случае, this, ссылаться на вновь созданный объект. Отрабатывает код функции, и объект возвращается.

Методы класса — также обычные функции. Вполне возможно определить методы внутри конструктора, но это было бы неправильно, так как каждый новый объект, созданный из конструктора, будет иметь копии определений методов. Более эффективно определить методы в прототипе конструктора, который существует именно для этого.

Тут стоит напомнить о прототипах и наследовании. Как только вы создали/определили функцию, так же создается специальный объект который будет находиться по ссылке имя_функции.prototype. После создания экземпляра объекта, у него будет ссылка __proto__ который ссылается на тот самый прототип.

То есть имя_функции.prototype === экземпляр.__proto__

Итак, делаем метод:

Именно ключевой оператор new задает свойство __proto__ у вновь созданного объекта ссылкой на свойство prototype функции конструктора. Таким вот образом, возможен вызов метода объекта, даже если он не находится в самом объекте. Движок не найдя метод в объекте, будет продолжать искать его в цепочке прототипов __proto__, даже если придется пройти путь до самого корневого элемента.

Более того, JavaScript использует цепочку прототипов для создания наследования. Чтобы класс Tesla наследовался от класса Car, мы определяем функцию конструктор Tesla, и вызываем функцию конструктор Car с текущим контекстом, указывающим на новый объект от Tesla. Это эквивалентно вызову super().

И еще раз, подробнее. В момент вызова Tesla с new — создается экземпляр — объект. this ссылается на этот объект. Далее вызывается конструктор Car посредством явного указания контекста call, и так как this ссылается на вновь созданные объект, все свойства конструктора Car применяются именно к новому объекту.

Но этого еще недостаточно. Необходимо связать прототип конструктора Tesla c прототипом конструктора Car. Используем Object.create(). Мы создадим у конструктора Tesla новый прототип который в свою очередь будет ссылаться (__proto__) на прототип конструктора Car.

Также необходимо восстановить свойство constructor указывающий на функцию конструктор, так как при создании прототипа мы затерли значение созданное при объявлении функции. Используем метод defineProperty для объявления свойства constructor, с дескриптором, свойства которого содержит “enumerable: false”, так как изначально свойство не является перечисляемым.

Наконец, мы можем определить метод charge() на прототипе конструктора Tesla.

Итак, создали экземпляр Tesla. Объект будет иметь свойства созданные конструктором Car и Tesla. При вызове метода drive(), движок не найдя его в объекте и прототипе объекта пойдет по цепочке __proto__ и найдет нужный метод в прототипе Car. Метод charge() уже находится в прототипе Tesla.

Заключение

Когда мы просто определяем класс в JS, многое происходит скрытно. Синтаксис очень похож на другие языки, но по факту — это совсем разные вещи. «Классы» — это объекты, а любой объект может наследовать свойства любого другого объекта.

новейшие классы JavaScript для большей гибкости

От автора: в этой статье мы рассмотрим новейшие классы JavaScript. Классы JavaScript — это особый тип функций. Однако они похожи на типичные функции в том, что классы JavaScript декларируются с ключевым словом и инициализируются синтаксисом выражения.

В JavaScript изначально не было классов. Классы были добавлены с введением ECMASCRIPT 6 (es6), новой и улучшенной версии JavaScript (ECMASCRIPT 5 — более старая версия).

Типичный класс JavaScript — это объект с методом конструктора по умолчанию. Классы JavaScript построены на прототипах, но с синтаксисом выражения.

До того, как классы появились, прототипы использовались для имитации классов в JavaScript. Прототип — это объект по умолчанию, прикрепленный к каждой функции и объекту JavaScript. К прототипу могут быть прикреплены дополнительные свойства, что помогает нам имитировать классы JavaScript.

Чтобы лучше понять это, давайте объявим функцию с именем car с двумя параметрами: age и name.

JavaScript. Быстрый старт

Изучите основы JavaScript на практическом примере по созданию веб-приложения

Узнать подробнее

function Car(){ this.name = ‘dragon’; this.age = 3; }

function Car(){

    this.name = ‘dragon’;

    this.age = 3;

}

Дополнительное свойство можно добавить с помощью прототипов, как вы увидите в блоке ниже:

Car.prototype.color = ‘white’

Car.prototype.color = ‘white’

Затем давайте создадим новый экземпляр автомобиля:

Теперь мы собираемся записать недавно добавленное свойство в консоль:

Функциональная машина JavaScript в этом случае служит классом с тремя свойствами: именем, возрастом и цветом. Другими словами, JavaScript использует наследование, которое поставляется с помощью прототипов для имитации классов.

ES6 классы

С введением классов в JavaScript ES6 предоставил нам гораздо более лаконичный способ объявления классов с использованием синтаксиса, аналогичного синтаксису других объектно-ориентированных языков программирования. В отличие от подхода ES5 к классам, ES6 не нуждается в ключевом слове function при работе с классами, хотя и скрытно. JavaScript по-прежнему считает классы особым типом функций.

Возможно, основное различие между классом и функцией заключается в подъеме: в отличие от функций, классы JavaScript должны быть объявлены перед доступом. В противном случае выдается ошибка.

Ключевое слово class

JavaScript предоставляет нам ключевое слово class, которое является основным способом определения классов. Оно служит синтаксическим сахаром к уже существующему шаблону наследования прототипов.

//javascript class declaration class Car { //methods }

//javascript class declaration

class Car  {

   //methods

}

Как показано выше, ключевое слово class используется для указания того, что определяется класс JavaScript. При определении класса можно следовать подходу, отличному от описанного выше, с использованием выражений класса для большей гибкости. Таким образом, класс может быть именованным или безымянным.

//unnamed javascript class expression let Car = class { constructor(name, age){ this.name = name this.age = age } }

//unnamed javascript class expression

let Car = class {

    constructor(name, age){

        this.name = name

        this.age = age

    }

}

Вот пример именованного выражения класса JavaScript:

//named javascript class expression let Car = class Car { constructor(name, age){ this.name = name this.age = age } }

//named javascript class expression

let Car = class Car {

    constructor(name, age){

        this.name = name

        this.age = age

    }

}

Метод конструктора

Метод конструктора — это специальный метод в JavaScript, используемый для инициализации объектов класса. Он вызывается автоматически в JavaScript после запуска класса. В любом классе JavaScript может быть только один метод конструктора.

JavaScript. Быстрый старт

Изучите основы JavaScript на практическом примере по созданию веб-приложения

Узнать подробнее

Если не определено, JavaScript добавляет к рассматриваемому классу пустой конструктор с нулевым параметром. Вот пример класса с методом конструктора:

//javascript class with a constructor class Car { constructor(name, age){ this.name = name this.age = age } }

//javascript class with a constructor

class Car {

    constructor(name, age){

        this.name = name

        this.age = age

    }

}

Приведенный выше класс содержит конструктор с двумя параметрами: имя и возраст.

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

Статический метод — это метод, который вызывается в самом классе, а не в экземпляре класса. Статический метод не является экземпляром класса, но он связан с ним с точки зрения функциональности. Вот типичный пример статического метода:

class Math { static add(number1 , number2){ return number1 + number2 } }

class Math {

    static add(number1 , number2){

        return number1 + number2

    }

}

Затем вышеупомянутый статический метод можно прописать так:

let additionResult = Math.add(2,3)

let additionResult = Math.add(2,3)

Обратите внимание, что статический метод add показывает нам, что означает добавление.

Синтаксис класса ES6

Типичный класс JavaScript имеет синтаксис, показанный ниже:

class Car { constructor(){ //default operation } method(){ //operation2 } }

class Car {

    constructor(){

        //default operation

    }

    method(){

        //operation2

    }

 

}

Проблемы с классами ES6

Классы могут предоставить более сложное решение для более простого способа выполнения определенных операций в JavaScript. Люди, имеющие опыт работы с объектно-ориентированным языком программирования, могут решить выполнять простые операции с помощью классов, даже если они не нужны.

Некоторые разработчики могут возразить, что добавление классов лишило JavaScript оригинальности и что использование прототипов было более гибким способом выполнения операций, требующих классов. Это связано с тем, что в отличие от классов в других объектно-ориентированных языках программирования, JavaScript не предоставляет базовых функций класса, таких как объявление частных переменных. ECMASCRIPT 2020 направлен на решение некоторых из этих проблем.

Дополнения к классам ECMASCRIPT 2020

Каждый год в JavaScript вносятся дополнения и изменения для более удобного использования JavaScript. Последние изменения находятся в ECMASCRIPT 2020. Некоторые из дополнений к классам на 2020 год включают частные переменные класса и статические поля.

Закрытая переменная класса: при работе с большой кодовой базой с большим количеством объявлений переменных может возникнуть необходимость в переменной, доступ к которой можно получить только внутри класса. Закрытая переменная класса решает эту проблему. Добавив хэш перед именем переменной, можно легко создать закрытую переменную.

class Detail { #name = «steve» welcome(){ console.log(this.#message) } } const detail_1 = new Detail() detail_1.welcome()

class Detail {

      #name = «steve»

     welcome(){

        console.log(this.#message)

      }

}

 

const detail_1 = new Detail()

   detail_1.welcome()

Вышеупомянутая переменная ‘#name’ доступна только в классе ‘Detail’.

Статические поля. Чтобы понять, что такое статические поля, рассмотрим приведенный ниже фрагмент кода:

class Detail { #name = «steven» welcome(){ console.log(this.#message) } }

class Detail {

     #name = «steven»

 

     welcome(){

         console.log(this.#message)

     }

}

В более старой версии JavaScript попытка получить доступ к методу welcome без создания нового экземпляра класса кажется невозможной. Но с последним дополнением мы можем получить доступ к этим методам без необходимости создания экземпляра.

Доступ к вышеуказанному методу можно получить так:

Заключение

Классы JavaScript решают некоторые проблемы, связанные с использованием прототипов. Они делают объявление класса более прямым и понятным. Новейший ECMASCRIPT 2020 делает использование классов еще проще, добавляя больше гибкости.

Автор: Amarachi Amaechi

Источник: blog.logrocket.com

Редакция: Команда webformyself.

JavaScript. Быстрый старт

Изучите основы JavaScript на практическом примере по созданию веб-приложения

Узнать подробнее

Full-Stack практика. Создание JavaScript блога

Создание веб-приложения с нуля на JavaScript, NodeJS, ExpressJS

Смотреть

ES6: классы изнутри — CSS-LIVE

Перевод статьи ES6 Classes in Depth  с сайта ponyfoo.com, опубликовано на css-live.ru с разрешения автора — Николаса Беваквы.

Встречайте «ES6 изнутри». Впервые здесь? Тогда, возможно, вам стоит изучить такие фичи ES6, как  деструктирование, литералы шаблона, стрелочные функции, оператор расширения и оставшиеся параметры или литерал объекта. Сегодня поговорим о «классах» в ES6.

Как и в прошлых статьях, рекомендую вам установить Babel и повторять за мной, копируя примеры с помощью REPL, либо командной строки babel-node и файла. Это поможет гораздо лучше усвоить идеи, обсуждаемые в серии. Если вы не из тех, кто любит устанавливать что-либо на свой компьютер, то вам есть смысл залезть на CodePen и кликнуть иконку с шестерёнкой для JavaScript — у него есть препроцессор Babel, который с лёгкостью позволяет опробовать ES6.

Начнём!

JavaScript — прототипно-ориентированный язык, что это еще за классы в ES6? Классы — синтаксический «сахар» поверх прототипного наследования — уловка, делающая язык притягательнее для программистов, пришедших из других парадигм, и, возможно, не совсем знакомых с цепочками прототипов. Многие фичи в ES6 (такие как деструктирование), по сути, синтаксический «сахар» — и классы не исключение. Я остановился на этом подробно, поскольку так нам будет легче понять базовую технологию, стоящую за классами в ES6. Саму структуру языка не переделывали, а просто упростили работу с прототипным наследованием для тех, кто привык к классам.

Хотя мне и не нравится термин «классы» для этой конкретной фичи, я вынужден признаться, что в действительности работать с этим синтаксисом гораздо легче, чем с синтаксисом обычного прототипного наследования в ES5, и это выигрыш для каждого — как их ни называй.

Теперь, когда с этим мы разобрались, я предположу, что вы понимаете прототипное наследование — иначе вы бы вряд ли читали блог о JavaScript. Вот как вы описали бы автомобиль Car, для которого можно создать экземпляр, заправить и запустить.

function Car () {
  this.fuel = 0;
  this.distance = 0;
}

Car.prototype.move = function () {
  if (this.fuel < 1) {
    throw new RangeError('Бензин закончился')
  }
  this.fuel--
  this.distance += 2
}

Car.prototype.addFuel = function () {
  if (this.fuel >= 60) {
    throw new RangeError('Бензобак заполнен')
  }
  this.fuel++
}

Чтобы запустить автомобиль, вы могли бы использовать следующий кусок кода.

var car = new Car()
car.addFuel()
car.move()
car.move()
// <- RangeError: 'Бензин закончился'

Отлично. А что на счёт классов в ES6? Синтаксис очень похож на объявление объекта, только здесь впереди подставляется class Name, где Name — название класса. Здесь мы используем нотацию сигнатуры метода, которую мы обсуждали вчера при объявлении методов с помощью сокращённого синтаксиса. constructor — такой же метод-конструктор, как в ES5, так что это можно использовать для инициализации любых переменных, которые могут быть у экземпляров.

class Car {
  constructor () {
    this.fuel = 0
    this.distance = 0
  }
  move () {
    if (this.fuel < 1) {
      throw new RangeError('Бензин закончился')
    }
    this.fuel--
    this.distance += 2
  }
  addFuel () {
    if (this.fuel >= 60) {
      throw new RangeError('Бензобак заполнен')
    }
    this.fuel++
  }
}

Если вы вдруг не заметили, запятые между свойствами и методами в классе недопустимы (почему так, для меня самого пока загадка), в отличие от литералов объекта, где запятые (по-прежнему) обязательны. Из-за этой разницы выбор между обычным литералом объекта и классом обернется для многих немалой головной болью, но надо признать, без запятых код и вправду смотрится почище.

У классов зачастую есть статические методы. Возьмите, к примеру, нашего старого приятеля Array. У каждого экземпляра массива есть его «персональные» методы .filter, .reduce и .map. Но и у «класса» Array есть свои статические методы, например, Array.isArray. Добавить подобные методы к «классу» Car и в ES5 довольно просто:

function Car () {
  this.topSpeed = Math.random()
}
Car.isFaster = function (left, right) {
  return left.topSpeed > right.topSpeed
}

В нотации ES6, с class, мы можем вставить перед методом static, по той же логике синтаксиса, что у get и set. Опять же, лишь «сахар» для ES5, поскольку перевести это в синтаксис старой версии не составляет труда.

class Car {
  constructor () {
    this.topSpeed = Math.random()
  }
  static isFaster (left, right) {
    return left.topSpeed > right.topSpeed
  }
}

Дополнительную сладость «сахарку» class в ES6 придает то, что в придачу к нему идет ключевое слово extends, дающее возможность легко «наследоваться» от других «классов». Мы знаем, что Тесла проезжает больше на том же количестве топлива, и в коде ниже видно, как класс Tesla расширяет класс Car (Tesla extends Car) и «переопределяет» (принцип, который может быть знаком вам по C#) метод move, позволяя покрыть большее расстояние.

class Tesla extends Car {
  move () {
    super.move()
    this.distance += 4
  }
}

Специальное ключевое слово super указывает на класс Car, от которого мы унаследовали — и раз уж мы упомянули C#, это сродни base. Смысл его существования в том, что чаще всего, когда мы переопределяем метод, заново реализуя его в наследуемом классе — класс Тесла в нашем примере — нам бывает нужно вызвать и метод базового класса. Таким образом нам не приходится заново копировать логику в наследуемый класс при каждом переопределении метода. Это было бы особенно паршиво, поскольку всякий раз при изменении базового класса нам бы пришлось переносить его логику в каждый наследуемый класс, превращая поддержку кода в сущий кошмар.

Теперь, если вы проделаете следующее, то заметите, что автомобиль Tesla проезжает две дистанции в силу base.move(), как обычная машина, и еще четыре таких же дистанции сверх, потому что это вам не что-нибудь, а Tesla.

var car = new Tesla()
car.addFuel()
car.move()
console.log(car.distance)
// <- 6

Чаще всего приходится переопределять метод constructor. Здесь можно просто вызвать super(), передавая любые аргументы, нужные базовому классу. Автомобили Тесла в два раза быстрее, так что мы просто вызываем конструктор базового класса Car с удвоенной заявленной скоростью speed.

class Car {
  constructor (speed) {
    this.speed = speed
  }
}
class Tesla extends Car {
  constructor (speed) {
    super(speed * 2)
  }
}

Завтра мы перейдём к синтаксису let, const и for ... of. Увидимся!

P.S. Это тоже может быть интересно:

Как использовать классы JavaScript

В 2015 году в стандарте ECMAScript 6 (ES6) были введены классы.

В JavaScript есть довольно необычный способ реализации наследования: прототипное наследование.Прототипное наследование, хотя, на мой взгляд, это здорово, в отличие от большинства других популярных языков программирования реализация наследования, основанная на классах.

Людям, пришедшим из Java, Python или других языков, было трудно понять тонкости прототипного наследования, поэтому комитет ECMAScript решил посыпать синтаксический сахар поверх прототипного наследования, чтобы оно напоминало то, как наследование на основе классов работает в других популярных реализациях.

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

Определение класса

Так выглядит класс.

class Person {
  constructor(name) {
    this.name = name
  }

hello() { return 'Hello, I am ’ + this.name + ‘.’ } }

У класса есть идентификатор, который мы можем использовать для создания новых объектов, используяnew ClassIdentifier().

Когда объект инициализируется,constructorвызывается метод с любыми переданными параметрами.

У класса также есть столько методов, сколько ему нужно. В этом случаеhelloявляется методом и может вызываться для всех объектов, производных от этого класса:

const flavio = new Person('Flavio')
flavio.hello()

Наследование классов

Класс может расширять другой класс, и объекты, инициализированные с помощью этого класса, наследуют все методы обоих классов.

Если у унаследованного класса есть метод с тем же именем, что и у одного из классов выше в иерархии, ближайший метод имеет приоритет:

class Programmer extends Person {
  hello() {
    return super.hello() + ' I am a programmer.'
  }
}

const flavio = new Programmer(‘Flavio’) flavio.hello()

(вышеприведенная программа печатает «Привет, я Флавио. Я программист.”)

Классы не имеют явных объявлений переменных класса, но вы должны инициализировать любую переменную в конструкторе.

Внутри класса вы можете ссылаться на вызов родительского классаsuper().

Статические методы

Обычно методы определяются в экземпляре, а не в классе.

Вместо этого в классе выполняются статические методы:

class Person {
  static genericHello() {
    return 'Hello'
  }
}

Person.genericHello() //Hello

Частные методы

В JavaScript нет встроенного способа определения частных или защищенных методов.

Есть обходные пути, но я не буду их здесь описывать.

Геттеры и сеттеры

Вы можете добавлять методы с префиксомgetили жеsetчтобы создать геттер и сеттер, которые представляют собой два разных фрагмента кода, которые выполняются в зависимости от того, что вы делаете: доступ к переменной или изменение ее значения.

class Person {
  constructor(name) {
    this._name = name
  }

set name(value) { this._name = value }

get name() { return this._name } }

Если у вас есть только метод получения, свойство не может быть установлено, и любая попытка сделать это (за пределами конструктора, который устанавливает значение при инициализации нового объекта этим классом) будет проигнорирована:

class Person {
  constructor(name) {
    this._name = name
  }

get name() { return this._name } }

Если у вас есть только сеттер, вы можете изменить значение, но не получить к нему доступ извне:

class Person {
  constructor(name) {
    this._name = name
  }

set name(value) { this._name = value } }

Геттеры и сеттеры очень полезны, когда вы хотите выполнить некоторый код при изменении значения свойства или если вы хотите создать «вычисляемое» свойство. Вы можете изменить возвращаемые значения с помощью геттера.

Вы также можете запустить некоторый код, например, вести журнал в консоли или в файле при изменении значения.


Больше руководств по js:

  • Чего следует избегать в JavaScript (плохие части)
  • Отсрочки и обещания в JavaScript (+ пример Ember.js)
  • Как загрузить файлы на сервер с помощью JavaScript
  • Стиль кодирования JavaScript
  • Введение в массивы JavaScript
  • Введение в язык программирования JavaScript
  • Полное руководство по ECMAScript 2015-2019
  • Понимание обещаний JavaScript
  • Лексическая структура JavaScript
  • Типы JavaScript
  • Переменные JavaScript
  • Список примеров идей веб-приложений
  • Введение в функциональное программирование с помощью JavaScript
  • Современный асинхронный JavaScript с Async и Await
  • Циклы и область действия JavaScript
  • Структура данных JavaScript карты
  • Заданная структура данных JavaScript
  • Руководство по шаблонным литералам JavaScript
  • Дорожная карта для изучения JavaScript
  • Выражения JavaScript
  • Откройте для себя таймеры JavaScript
  • Объяснение событий JavaScript
  • Циклы JavaScript
  • Пишите циклы JavaScript, используя map, filter, reduce и find
  • Цикл событий JavaScript
  • Функции JavaScript
  • Глоссарий JavaScript
  • Замыкания JavaScript объяснены
  • Учебник по функциям стрелок в JavaScript
  • Руководство по регулярным выражениям JavaScript
  • Как проверить, содержит ли строка подстроку в JavaScript
  • Как удалить элемент из массива в JavaScript
  • Как глубоко клонировать объект JavaScript
  • Introduction to Unicode and UTF-8
  • Юникод в JavaScript
  • Как ввести первую букву строки в верхний регистр в JavaScript
  • Как отформатировать число как денежное значение в JavaScript
  • Как преобразовать строку в число в JavaScript
  • это в JavaScript
  • Как получить текущую метку времени в JavaScript
  • Строгий режим JavaScript
  • Выражения функции немедленного вызова JavaScript (IIFE)
  • Как перенаправить на другую веб-страницу с помощью JavaScript
  • Как удалить свойство из объекта JavaScript
  • Как добавить элемент в массив в JavaScript
  • Как проверить, не определено ли свойство объекта JavaScript
  • Введение в модули ES
  • Введение в CommonJS
  • Асинхронное программирование и обратные вызовы JavaScript
  • Как заменить все вхождения строки в JavaScript
  • Краткое справочное руководство по современному синтаксису JavaScript
  • Как обрезать ведущий ноль в числе в JavaScript
  • Как проверить объект JavaScript
  • Полное руководство по датам JavaScript
  • Учебник Moment.js
  • Точка с запятой в JavaScript
  • Арифметические операторы JavaScript
  • Объект JavaScript Math
  • Создавайте случайные и уникальные строки в JavaScript
  • Как заставить ваши функции JavaScript спать
  • Прототипное наследование JavaScript
  • Исключения JavaScript
  • Как использовать классы JavaScript
  • Поваренная книга JavaScript
  • Цитаты в JavaScript
  • Как проверить адрес электронной почты в JavaScript
  • Как получить уникальные свойства набора объектов в массиве JavaScript
  • Как проверить, начинается ли строка с другой в JavaScript
  • Как создать многострочную строку в JavaScript
  • Руководство по ES6
  • Как получить текущий URL в JavaScript
  • Руководство ES2016
  • Как инициализировать новый массив значениями в JavaScript
  • Руководство ES2017
  • Руководство ES2018
  • Как использовать Async и Await с Array.prototype.map ()
  • Асинхронный и синхронный код
  • Как сгенерировать случайное число между двумя числами в JavaScript
  • Учебное пособие по HTML Canvas API
  • Как получить индекс итерации в цикле for-of в JavaScript
  • Что такое одностраничное приложение?
  • Введение в WebAssembly
  • Введение в JSON
  • Руководство по JSONP
  • Should you use or learn jQuery in 2020?
  • Как скрыть элемент DOM с помощью простого JavaScript
  • Как объединить два объекта в JavaScript
  • Как очистить массив JavaScript
  • Как закодировать URL-адрес с помощью JavaScript
  • Как установить значения параметров по умолчанию в JavaScript
  • Как отсортировать массив объектов по значению свойства в JavaScript
  • Как подсчитать количество свойств в объекте JavaScript
  • call () и apply () в JavaScript
  • Введение в PeerJS, библиотеку WebRTC
  • Работа с объектами и массивами с помощью Rest и Spread
  • Разрушение объектов и массивов в JavaScript
  • Полное руководство по отладке JavaScript
  • Руководство по TypeScript
  • Динамически выбирать метод объекта в JavaScript
  • Передача undefined в JavaScript с немедленным вызовом функциональных выражений
  • Свободно типизированные языки против строго типизированных языков
  • Как стилизовать элементы DOM с помощью JavaScript
  • Трансляция в JavaScript
  • Руководство по генераторам JavaScript
  • Размер папки node_modules не является проблемой. Это привилегия
  • Как решить непредвиденную ошибку идентификатора при импорте модулей в JavaScript
  • Как перечислить все методы объекта в JavaScript
  • Метод String replace ()
  • Метод String search ()
  • Как я запускаю небольшие фрагменты кода JavaScript
  • Руководство ES2019
  • Метод String charAt ()
  • Метод String charCodeAt ()
  • Метод String codePointAt ()
  • Метод String concat ()
  • Метод String EndWith ()
  • Метод String includes ()
  • Метод String indexOf ()
  • Метод String lastIndexOf ()
  • Метод String localeCompare ()
  • Метод String match ()
  • Метод String normalize ()
  • Метод String padEnd ()
  • Метод String padStart ()
  • Метод String repeat ()
  • Метод String slice ()
  • Метод String split ()
  • Метод String startWith ()
  • Метод String substring ()
  • Метод String toLocaleLowerCase ()
  • Метод String toLocaleUpperCase ()
  • Метод String toLowerCase ()
  • Метод String toString ()
  • Метод String toUpperCase ()
  • Метод String trim ()
  • Метод String trimEnd ()
  • Метод String trimStart ()
  • Мемоизация в JavaScript
  • Метод String valueOf ()
  • Ссылка на JavaScript: Строка
  • Метод Number isInteger ()
  • Метод Number isNaN ()
  • Метод Number isSafeInteger ()
  • Метод Number parseFloat ()
  • Метод Number parseInt ()
  • Метод Number toString ()
  • Метод Number valueOf ()
  • Метод Number toPrecision ()
  • Метод Number toExponential ()
  • Метод Number toLocaleString ()
  • Метод Number toFixed ()
  • Метод Number isFinite ()
  • Ссылка на JavaScript: номер
  • Дескрипторы свойств JavaScript
  • Метод Object assign ()
  • Метод Object create ()
  • Метод Object defineProperties ()
  • Метод Object defineProperty ()
  • Метод записи объекта ()
  • Метод Object freeze ()
  • Метод Object getOwnPropertyDescriptor ()
  • Метод Object getOwnPropertyDescriptors ()
  • Метод Object getOwnPropertyNames ()
  • Метод Object getOwnPropertySymbols ()
  • Метод Object getPrototypeOf ()
  • Метод Object is ()
  • Метод Object isExtensible ()
  • Метод Object isFrozen ()
  • Метод Object isSealed ()
  • Метод Object keys ()
  • Метод Object preventExtensions ()
  • Метод Object seal ()
  • Метод Object setPrototypeOf ()
  • Метод значений объекта ()
  • Метод Object hasOwnProperty ()
  • Метод Object isPrototypeOf ()
  • Метод Object propertyIsEnumerable ()
  • Метод Object toLocaleString ()
  • Метод Object toString ()
  • Метод Object valueOf ()
  • Справка по JavaScript: объект
  • Оператор присваивания JavaScript
  • Интернационализация JavaScript
  • Оператор типа JavaScript
  • Новый оператор JavaScript
  • Операторы сравнения JavaScript
  • Правила приоритета операторов JavaScript
  • Оператор instanceof в JavaScript
  • Заявления JavaScript
  • Область действия JavaScript
  • Преобразования типов JavaScript (приведение)
  • Операторы равенства JavaScript
  • Условное выражение if / else в JavaScript
  • Условное переключение JavaScript
  • Оператор удаления JavaScript
  • Параметры функции JavaScript
  • Оператор распространения JavaScript
  • Возвращаемые значения JavaScript
  • Логические операторы JavaScript
  • Тернарный оператор JavaScript
  • Рекурсия JavaScript
  • Свойства объекта JavaScript
  • Объекты ошибок JavaScript
  • Глобальный объект JavaScript
  • Функция JavaScript filter ()
  • Функция JavaScript map ()
  • Функция JavaScript reduce ()
  • Оператор `in` в JavaScript
  • Операторы JavaScript
  • Как получить значение свойства CSS в JavaScript
  • Как добавить прослушиватель событий к нескольким элементам в JavaScript
  • Поля частного класса JavaScript
  • Как отсортировать массив по значению даты в JavaScript
  • Поля открытого класса JavaScript
  • Символы JavaScript
  • Как использовать библиотеку JavaScript bcrypt
  • Как переименовывать поля при деструктуризации объекта
  • Как проверять типы в JavaScript без использования TypeScript
  • Как проверить, содержит ли массив JavaScript определенное значение
  • При чем тут оператор двойного отрицания !! делать в JavaScript?
  • Какой оператор равенства следует использовать при сравнении JavaScript? == против ===
  • Стоит ли изучать JavaScript?
  • Как вернуть результат асинхронной функции в JavaScript
  • Как проверить, пустой ли объект в JavaScript
  • Как выйти из цикла for в JavaScript
  • Как добавить элемент в массив по определенному индексу в JavaScript
  • Почему не следует изменять прототип объекта JavaScript
  • В чем разница между использованием let и var в JavaScript?
  • Ссылки, используемые для активации функций JavaScript
  • Как соединить две строки в JavaScript
  • Как соединить два массива в JavaScript
  • Как проверить, является ли значение JavaScript массивом?
  • Как получить последний элемент массива в JavaScript?
  • Как отправить urlencoded данные с помощью Axios
  • Как получить дату завтрашнего дня с помощью JavaScript
  • Как получить вчерашнюю дату с помощью JavaScript
  • Как получить название месяца из даты JavaScript
  • Как проверить, совпадают ли две даты в один и тот же день в JavaScript
  • Как проверить, относится ли дата к дню в прошлом в JavaScript
  • Операторы с пометкой JavaScript
  • Как дождаться выполнения 2 или более обещаний в JavaScript
  • Как получить дни между двумя датами в JavaScript
  • Как загрузить файл с помощью Fetch
  • Как отформатировать дату в JavaScript
  • Как перебирать свойства объекта в JavaScript
  • Как рассчитать количество дней между двумя датами в JavaScript
  • Как использовать ожидание верхнего уровня в модулях ES
  • Динамический импорт JavaScript
  • Необязательная цепочка JavaScript
  • Как заменить пробел внутри строки в JavaScript
  • Нулевое объединение JavaScript
  • Как сгладить массив в JavaScript
  • Это десятилетие в JavaScript
  • Как отправить заголовок авторизации с помощью Axios
  • Список ключевых слов и зарезервированных слов в JavaScript
  • Как преобразовать массив в строку в JavaScript
  • Как удалить все содержимое папок node_modules
  • Как удалить дубликаты из массива JavaScript
  • Let vs Const в JavaScript
  • Один и тот же вызов POST API в различных библиотеках JavaScript.
  • Как получить первые n элементов массива в JS
  • Как разделить массив на несколько равных частей в JS
  • Как замедлить цикл в JavaScript
  • Как загрузить изображение на холст HTML
  • Как разрезать строку на слова в JavaScript
  • Как разделить массив пополам в JavaScript
  • Как написать текст на холсте HTML
  • Как удалить последний символ строки в JavaScript
  • Как удалить первый символ строки в JavaScript
  • Как исправить ошибку TypeError: невозможно назначить только для чтения свойство «exports» объекта «# & lt; Object & gt;» ошибка
  • Как создать всплывающее окно с намерением выхода
  • Как проверить, является ли элемент потомком другого
  • Как принудительно вводить учетные данные для каждого запроса Axios
  • Как устранить ошибку «не функция» в JavaScript
  • Гэтсби, как изменить фавикон
  • Загрузка внешнего файла JS с помощью Gatsby
  • Как определить темный режим с помощью JavaScript
  • Посылка, как исправить ошибку `регенераторВремя выполнения не определено`
  • Как определить, используется ли блокировщик рекламы с JavaScript
  • Деструктуризация объектов с типами в TypeScript
  • Справочник Deno: краткое введение в Deno 🦕
  • Как получить последний сегмент пути или URL-адреса с помощью JavaScript
  • Как перемешать элементы в массиве JavaScript
  • Как проверить, существует ли ключ в объекте JavaScript
  • Возбуждение событий и захват событий
  • event.stopPropagation против event.preventDefault () против return false в событиях DOM
  • Примитивные типы и объекты в JavaScript
  • Как узнать, к какому типу относится значение в JavaScript?
  • Как вернуть несколько значений из функции в JavaScript
  • Стрелочные функции и обычные функции в JavaScript
  • Как мы можем получить доступ к значению свойства объекта?
  • В чем разница между null и undefined в JavaScript?
  • В чем разница между методом и функцией?
  • Как мы можем выйти из цикла в JavaScript?
  • Цикл for..of в JavaScript
  • Что такое деструктуризация объектов в JavaScript?
  • Что такое подъем в JavaScript?
  • Как заменить запятые на точки с помощью JavaScript
  • Важность тайминга при работе с DOM
  • Как перевернуть массив JavaScript
  • Как проверить, является ли значение числом в JavaScript
  • Как принять неограниченное количество параметров в функции JavaScript
  • Объекты прокси JavaScript
  • Делегирование событий в браузере с использованием ванильного JavaScript
  • Ключевое слово JavaScript super
  • Введение в XState
  • Значения передаются по ссылке или по значению в JavaScript?
  • Пользовательские события в JavaScript
  • Пользовательские ошибки в JavaScript
  • Пространства имен в JavaScript
  • Любопытное использование запятых в JavaScript
  • Цепочка вызовов методов в JavaScript
  • Как справиться с отклонением обещаний
  • Как поменять местами два элемента массива в JavaScript
  • Как я исправил ошибку «cb.apply is not a function» при использовании Gitbook
  • Как добавить элемент в начало массива в JavaScript
  • Гэтсби, исправьте ошибку «не удается найти модуль gatsby-cli / lib / reporter»
  • Как получить индекс элемента в массиве JavaScript
  • Как проверить пустой объект в JavaScript
  • Как деструктурировать объект до существующих переменных в JavaScript
  • Структура данных JavaScript в виде массива
  • Структура данных Stack JavaScript
  • Структуры данных JavaScript: очередь
  • Структуры данных JavaScript: Установить
  • Структуры данных JavaScript: словари
  • Структуры данных JavaScript: связанные списки
  • JavaScript, как экспортировать функцию
  • JavaScript, как экспортировать несколько функций
  • JavaScript, как выйти из функции
  • JavaScript, как найти символ в строке
  • JavaScript, как фильтровать массив
  • JavaScript, как расширить класс
  • JavaScript, как найти дубликаты в массиве
  • JavaScript, как заменить элемент массива
  • Алгоритмы JavaScript: линейный поиск
  • Алгоритмы JavaScript: двоичный поиск
  • Алгоритмы JavaScript: сортировка выбора
  • Алгоритмы JavaScript: быстрая сортировка
  • Алгоритмы JavaScript: сортировка слиянием
  • Алгоритмы JavaScript: пузырьковая сортировка

ООП в JavaScript | Frontend Stuff

Когда речь идет об объектно-ориентированном программировании — подразумевается моделирование реальных объектов и отношений.

В ООП объект представляет собой блок, содержащий информацию (состояние / атрибуты) и операции (методы).

Ключевое слово

this
  • this — это объект, свойством которого является функция;
  • this — дает функциям доступ к своему объекту и его свойствам;
  • this — помогает выполнить один и тот же код для нескольких объектов;
  • this — можно рассматривать как кто меня вызвал?; т.е. то, что находится слева от точки. Например, window.a();
  • this — имеет динамическую область, т. е. не важно, где он был написан, важно, где он был вызван.
const obj = {
  name: 'Alex',
  sing() {
    console.log('a this ', this);
    var anotherFunc = function() {
      console.log('b this ', this);
    }
    anotherFunc();
  }
};

obj.sing();


var b = {
  name: 'jay',
  say() {
    console.log('this is ', this);
  }
}
b.say()


var c = {
  name: 'jay',
  say() {
    return function () {
      console.log('this is ', this);
    }
  }
}
c.say()()


var d = {
  name: 'jay',
  say() {
    
    return () => console.log('this is ', this);
  }
}
d.say()()

Стрелочные функции связывают this с лексической областью действия.

const obj = {
  name: 'Alex',
  sing() {
    console.log('a this ', this);
    var anotherFunc = () => {
      console.log('b this ', this);
    }
    anotherFunc();
  }
};

obj.sing();


Прототип

  • Прототип (prototype) — это экземпляр рабочего объекта. Объекты наследуются напрямую от других объектов.
  • __proto__ является ссылкой на свойство прототипа родительского объекта, например:
const obj = {};
obj.__proto__ === Object.prototype 
  • Свойство prototype принадлежит только функциям, в частности, функциям конструктора. Конструктор Object создает обертку объекта.
  • Свойства proto и prototype используются для создания цепочки наследования свойств между объектами, начиная с Object и Primitive Types.
  • Object.create() можно использовать для создания объектов с его свойством proto, связанным со свойством prototype объекта, переданного в качестве аргумента Object.create().
  • Object — это базовая функция (конструктор). Корнем всего в JavaScript является Object, который на самом деле является функцией.

Object имеет свойство prototype, которое является базовым объектом для всех вещей в JavaScript, включая функции JavaScript.

ES6 Классы

  • Ключевое слово class в JS — синтаксический сахар. Под капотом он всё еще использует прототипное наследование (prototypal inheritance).
  • Экземпляры класса должны создаваться с ключевым словом new.
  • Метод constructor используется для создания экземпляра state (данных) нового объекта. State обычно уникально для каждого экземпляра.
  • Функции обычно не включаются в конструктор, так как они создают ссылку на место в памяти в каждом новом экземпляре класса. Таким образом используя больше памяти, чем необходимо. Включая функции в качестве методов класса, экземпляры класса могут ссылаться на функцию через цепочку прототипов.
  • Прототипное наследование (prototypal inheritance) имеет лучшую эффективность памяти, чем классическое наследование, благодаря тому, что оно разделяет ссылки памяти своих свойств прототипа с теми объектами, которые наследуют от него. В классическом наследовании, экземпляры класса создают новые ссылки на память для каждого унаследованного свойства.

Object.create() vs. Classes

  • Оба Object.create() и class являются способами создания цепочки прототипов.
  • Некоторые люди предпочитают избегать ключевые слова constructor, class и this, чтобы ограничить путаницу из-за this.
  • Другие предпочитают использовать ключевые слова constructor, class и this, возможно, из-за его сходства с другими языками с парадигмой объектно-ориентированного программирования.

Private vs. Public vs. Protected

Во многих объектно-ориентированных языках программирования, которые имеют классы, идея private и public полей действительно важна. В JavaScript этого нет. Ранее, если нужно было сделать поле private, к которому нельзя обращаться из класса, мы добавляли подчеркивание _ перед именем, чтобы другие программисты знали, что это private метод. Но, к сожалению, подчеркивание на самом деле ничего не делает.

В JavaScript есть предложение ECMAScript, которое предназначено для объявлений полей класса.

Это модификаторы доступа, которые помогают нам реализовать Encapsulation (или скрытие информации). Они сообщают компилятору, какие другие классы должны иметь доступ к определенному полю или методу.

Private — только текущий класс будет иметь доступ к полю или методу. Protected — только текущий класс и подклассы этого класса будут иметь доступ к полю или методу. Public — любой класс может ссылаться на поле или вызывать метод.

Так как в Javascript таких полей пока нет, для их реализации мы можем использовать TypeScript.

4 принципа ООП

Инкапсуляция

Инкапсуляция включает в себя идею о том, что данные объекта не должны быть напрямую доступны. Нужно вызывать методы вместо прямого доступа к данным. Инкапсуляция позволяет нам скрывать/показывать свойства функций.

ООП заключаем код в блоки, которые связаны друг с другом, чтобы эти блоки могли просто взаимодействовать друг с другом, используя методы и свойства, которые мы делаем доступными. Данный принцып делает код проще в обслуживании и более пригодным для повторного использования.

Инкапсуляция с использованием замыкания
const createCounter = () => {
  
  
  let count = 0;

  return ({
    
    
    
    click: () => count += 1,
    getCount: () => count.toLocaleString()
  });
};

const counter = createCounter();

counter.click();
counter.click();
counter.click();

console.log(counter.getCount()); 

Абстракция

Абстракция — это способ создания простой модели, которая содержит только важные свойства с точки зрения контекста приложения, из более сложной модели. Иными словами — это способ скрыть детали реализации и показать пользователям только функциональность. Абстракция игнорирует нерелевантные детали и показывает только необходимые. Важно помнить, что мы не можем создать экземпляр абстрактного класса.

Всё программное обеспечение — это абстракция, скрывающая всю тяжелую работу и бездумные детали.

Многие программные процессы повторяются снова и снова. Поэтому, на этапе декомпозиции проблемы, мы удалим дублирование, записывая какой-либо компонент (функцию, модуль, класс и т. Д.), присваивая ему имя (идентификатор) и повторно используя его столько раз, сколько нам нужно.

Процесс декомпозиции — это процесс абстракции. Успешная абстракция подразумевает, что результатом является набор независимо полезных и перекомпонованных компонентов.

Полиморфизм

Полиморфизмом является одним из принципов объектно-ориентированного программирования (ООП). Это помогает проектировать объекты таким образом, чтобы они могли совместно использовать или переопределять любое поведение с конкретными предоставленными объектами.

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

Чтобы это произошло полиморфизм использует наследование.

В следующем примере дочерний объект, такой как Coder, переопределяет метод say, вызванный из родительского объекта Human, и возвращает новую строку соответственно. Тогда как другой дочерний объект Men, вместо переопределения метода say, наследует его и отображал родительскую строку.

class Human {
  constructor(name) {
    this.name = name;
  }

  say() {
    return `Hello, my name is ${this.name}, I like travelling`;
  }
}

class Men extends Human {
  constructor(name) {
    super(name)
  }
  
}

class Coder extends Human {
  constructor(name) {
    super(name)
  }

  say() {
    
    return `Hello, my name is ${this.name}, I like coding`;
  }
}

const alex = new Men('Alex');
const leo = new Coder('Leo');

alex.say() 
leo.say() 

Наследование

Наследование — это механизм базирования объекта или class на другом объекте (наследование на основе прототипа) или class (наследование на основе класса). Мы избегаем необходимости переписывать один и тот же код, а также экономим пространство памяти, используя общие методы.

class Human {
  constructor(name) {
    this.name = name;
  }

  sayMyName() {
    return 'Hello, I am ' + this.name;
  }
}

class Men extends Human {
  constructor(name) {
    super(name)
  }
}
class Coder extends Human {
  constructor(name) {
    super(name)
  }
}

const alex = new Men('Alex');
const leo = new Coder('Leo');

alex.sayMyName() 
leo.sayMyName() 
Prototypal Inheritance

Программирование на основе прототипов — это стиль объектно-ориентированного программирования, в котором повторное использование поведения (известное как наследование) выполняется через процесс повторного использования существующих объектов посредством делегирования, которые служат как prototypes. Сторонники программирования на основе прототипов утверждают, что данный стиль поощряет программиста сосредоточиться на поведении некоторого набора примеров и лишь позднее, беспокоиться о классификации этих объектов в архетипические объекты, которые впоследствии используются аналогично классам.

Classical Inheritance

Программирование на основе классов, или же, ориентация на классы, — это стиль объектно-ориентированного программирования (ООП), в котором наследование происходит через определение классов объектов, вместо наследования, которое происходит только через объекты.

Tight Coupling (сильная связанность) относится к волновым эффектам, которые могут произойти с подклассами (дочерние классы), когда вносится изменение в суперкласс (родительский класс).

  • Tight Coupling может привести ко многим непреднамеренным эффектам для подклассов.
  • Tight Coupling может помочь избежать повторений в коде.
  • Хрупкая проблема базового класса является фундаментальной архитектурной проблемой систем объектно-ориентированного программирования, в которых базовые классы (суперклассы) считаются «хрупкими», потому что, казалось бы, безопасные модификации базового класса, когда они наследуются производным классом, могут привести к сбоям в работе производных классов.
  • Проблема гориллы с бананом относится к проблеме наследования слишком много от суперкласса. «Будто тебе нужен банан, а ты получаешь банан в придачу с гориллой в джунглях».
  • Классическое наследование требует превосходного предвидения, чтобы избежать проблем неправильного наследования.

Учебник по JavaScript: ч.1: Классы: ilyachalov — LiveJournal

Прочел девятый раздел («Классы») первой части («Язык программирования JavaScript») учебника по JavaScript.

https://learn.javascript.ru

Часть 1. Язык программирования JavaScript (в т.ч. 93 подраздела)

Разделы:

9. Классы (7 подразделов)

9.1 Класс: базовый синтаксис
9.2 Наследование классов
9.3 Статические свойства и методы
9.4 Приватные и защищённые методы и свойства
9.5 Расширение встроенных классов
9.6 Проверка класса: «instanceof»
9.7 Примеси

Ранее я уже написал два поста, касающихся подраздела 9.1 учебника:
1. JavaScript: класс VS функция-конструктор
2. JavaScript: класс VS функция-конструктор (визуализация)

Если в части работы с функциями язык JavaScript показался мне очень хорош, то в части работы с классами он выглядит еще очень сырым (хотел написать, что язык JavaScript — молодой, но он родился, как я понимаю, в 1995 году… так что не такой уж он и молодой). Многие вещи, описываемые в разделе 9 учебника, еще даже не включены в спецификацию языка, а являются экспериментальными (реализованы в некоторых популярных браузерах).

Например, это касается статических свойств и приватных свойств класса.

Защищённые свойства реализованы не на уровне языка, а на уровне соглашения (хоть и общеизвестного) между программистами!

Возможность множественного наследования в языке JavaScript отсутствует. В качестве альтернативы предлагается механизм примесей. Примесь (по-английски «mix in») — это класс (в подразделе 9.7 учебника вместо классов в качестве примеров примесей приведены объекты), который не предназначен для создания собственных объектов, а содержит методы, которые можно скопировать в другой класс, придав таким образом этому другому классу дополнительную функциональность.

У каждой из альтернатив (примеси и множественное наследование) имеются свои плюсы и свои минусы. В википедии есть статья, посвященная примесям в программировании:
https://ru.wikipedia.org/wiki/Примесь_(программирование)

классов — JavaScript | MDN

Классы — это шаблон для создания объектов. Они инкапсулируют данные с помощью кода для работы с этими данными. Классы в JS построены на прототипах, но также имеют некоторый синтаксис и семантику, которые не разделяются с семантикой класса ES5.

Объявления классов

Одним из способов определения класса является использование объявления класса . Чтобы объявить класс, вы используете ключевое слово class с именем класса (здесь «Прямоугольник»).

  class Rectangle {
  конструктор (высота, ширина) {
    это.высота = высота;
    this.width = width;
  }
}
  
Подъемник

Важное различие между объявлениями функций и объявлениями классов состоит в том, что объявления функций поднимаются, а объявления классов — нет. Сначала вам нужно объявить свой класс, а затем получить к нему доступ, иначе код, подобный следующему, вызовет ReferenceError :

  const p = новый прямоугольник ();

class Rectangle {}
  

Выражения класса

Выражение класса — это еще один способ определения класса.Выражения класса могут быть именованными или безымянными. Имя, данное выражению именованного класса, является локальным для тела класса. Однако к нему можно получить доступ через свойство name .

 
let Rectangle = class {
  конструктор (высота, ширина) {
    this.height = высота;
    this.width = width;
  }
};
console.log (Rectangle.name);



let Rectangle = class Rectangle2 {
  конструктор (высота, ширина) {
    this.height = высота;
    this.width = width;
  }
};
console.log (Rectangle.name);

  

Примечание. Выражения класса подчиняются тем же ограничениям подъема, которые описаны в разделе объявлений классов.

Тело класса — это часть, заключенная в фигурные скобки {} . Здесь вы определяете члены класса, такие как методы или конструктор.

Строгий режим

Тело класса выполняется в строгом режиме, то есть код, написанный здесь, подлежит более строгому синтаксису для повышения производительности, в противном случае будут возникать некоторые скрытые ошибки, а некоторые ключевые слова зарезервированы для будущих версий ECMAScript.

Конструктор

Метод конструктора — это специальный метод для создания и инициализации объекта, созданного с помощью класса .В классе может быть только один специальный метод с именем «конструктор». SyntaxError будет выдано, если класс содержит более одного вхождения метода конструктора .

Конструктор может использовать ключевое слово super для вызова конструктора суперкласса.

Методы прототипа

См. Также определения методов.

  class Rectangle {
  конструктор (высота, ширина) {
    this.height = высота;
    this.width = width;
  }
  
  get area () {
    верни это.calcArea ();
  }
  
  calcArea () {
    вернуть this.height * this.width;
  }
}

const square = новый прямоугольник (10, 10);

console.log (квадратная область);
  

Генераторные методы

См. Также Итераторы и генераторы.

  class Polygon {
  constructor (... сторон) {
    this.sides = стороны;
  }
  
  * getSides () {
    for (const side of this.sides) {
      сторона уступки;
    }
  }
}

const pentagon = новый многоугольник (1,2,3,4,5);

console.log ([... pentagon.getSides ()]);
  

Статические методы и свойства

Ключевое слово static определяет статический метод или свойство для класса.Статические члены (свойства и методы) вызываются без создания экземпляра своего класса, а не может вызывать через экземпляр класса. Статические методы часто используются для создания служебных функций для приложения, тогда как статические свойства полезны для кешей, фиксированной конфигурации или любых других данных, которые не нужно реплицировать между экземплярами.

  класс Point {
  constructor (x, y) {
    this.x = x;
    this.y = y;
  }

  static displayName = "Point";
  статическое расстояние (a, b) {
    const dx = а.х - b.x;
    const dy = a.y - b.y;

    вернуть Math.hypot (dx, dy);
  }
}

const p1 = новая точка (5, 5);
const p2 = новая точка (10, 10);
p1.displayName;
p1.distance;
p2.displayName;
p2.distance;

console.log (Point.displayName);
console.log (Point.distance (p1, p2));
  

Связывание

этого с прототипом и статическими методами

Когда статический метод или метод прототипа вызывается без значения для этого , например, путем присвоения метода переменной и последующего ее вызова, это значение будет быть undefined внутри метода.Это поведение будет таким же, даже если директива "use strict" отсутствует, потому что код в пределах синтаксической границы тела class всегда выполняется в строгом режиме.

  class Animal {
  говорить() {
    вернуть это;
  }
  static eat () {
    вернуть это;
  }
}

let obj = new Animal ();
obj.speak ();
пусть говорят = obj.speak;
говорить();

Animal.eat ()
пусть есть = Animal.eat;
есть();
  

Если мы перепишем приведенное выше, используя традиционный синтаксис, основанный на функциях, в нестрогом режиме, то вызовы этого метода автоматически привязываются к начальному значению this , которое по умолчанию является глобальным объектом.В строгом режиме автосвязывания не произойдет; значение остается прежним.

  функция Animal () {}

Animal.prototype.speak = function () {
  вернуть это;
}

Animal.eat = function () {
  вернуть это;
}

let obj = new Animal ();
пусть говорят = obj.speak;
говорить();

пусть есть = Animal.eat;
есть();
  

Свойства экземпляра

Свойства экземпляра должны быть определены внутри методов класса:

  class Rectangle {
  конструктор (высота, ширина) {
    это.высота = высота;
    this.width = width;
  }
}
  

Объявления полей

Объявления общедоступных полей

С синтаксисом объявления поля JavaScript приведенный выше пример можно записать как:

  class Rectangle {
  высота = 0;
  ширина;
  конструктор (высота, ширина) {
    this.height = высота;
    this.width = width;
  }
}
  

За счет предварительного объявления полей определения классов становятся более самодокументированными, и поля присутствуют всегда.

Как видно выше, поля могут быть объявлены со значением по умолчанию или без него.

Дополнительные сведения см. В полях общедоступных классов.

Объявления частного поля

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

  class Rectangle {
  #height = 0;
  #ширина;
  конструктор (высота, ширина) {
    это. # height = height;
    это. # width = width;
  }
}
  

Ссылка на частные поля извне класса - ошибка; они могут быть прочитаны или записаны только в теле класса.Определяя вещи, которые не видны за пределами класса, вы гарантируете, что пользователи ваших классов не могут зависеть от внутренних компонентов, которые могут меняться от версии к версии.

Примечание: Частные поля могут быть объявлены только заранее в объявлении поля.

Частные поля не могут быть созданы позже путем назначения им, как это могут делать обычные свойства.

Для получения дополнительной информации см. Функции частного класса.

Ключевое слово extends используется в объявлениях классов или выражениях классов для создания класса как потомка другого класса.

  class Animal {
  конструктор (имя) {
    this.name = имя;
  }

  говорить() {
    console.log (`$ {this.name} шумит .`);
  }
}

class Dog расширяет Animal {
  конструктор (имя) {
    супер (имя);
  }

  говорить() {
    console.log (`$ {this.name} barks.`);
  }
}

пусть d = новая собака ('Митци');
d.speak ();
  

Если в подклассе присутствует конструктор, он должен сначала вызвать super () перед использованием this.

Можно также расширить традиционные функциональные "классы":

  function Animal (name) {
  это.name = name;
}

Animal.prototype.speak = function () {
  console.log (`$ {this.name} шумит .`);
}

class Dog расширяет Animal {
  говорить() {
    console.log (`$ {this.name} barks.`);
  }
}

пусть d = новая собака ('Митци');
d.speak ();


  

Обратите внимание, что классы не могут расширять обычные (неконструируемые) объекты. Если вы хотите наследовать от обычного объекта, вы можете вместо этого использовать Object.setPrototypeOf () :

  const Animal = {
  говорить() {
    console.log (`$ {this.name} шумит. `);
  }
};

class Dog {
  конструктор (имя) {
    this.name = имя;
  }
}


Object.setPrototypeOf (Dog.prototype, Животное);

пусть d = новая собака ('Митци');
d.speak ();
  

Возможно, вы захотите вернуть объектов Array в производном классе массива MyArray . Шаблон разновидностей позволяет вам переопределить конструкторы по умолчанию.

Например, при использовании таких методов, как map () , которые возвращают конструктор по умолчанию, вы хотите, чтобы эти методы возвращали родительский объект Array вместо объекта MyArray .Это можно сделать с помощью символа Symbol.species :

  class MyArray extends Array {
  
  статический get [Symbol.species] () {return Array; }
}

пусть a = новый MyArray (1,2,3);
пусть mapped = a.map (x => x * x);

console.log (сопоставленный экземпляр MyArray);
console.log (сопоставленный экземпляр массива);
  

Ключевое слово super используется для вызова соответствующих методов суперкласса. Это одно из преимуществ перед наследованием на основе прототипов.

  class Cat {
  конструктор (имя) {
    это.name = name;
  }

  говорить() {
    console.log (`$ {this.name} шумит .`);
  }
}

class Lion extends Cat {
  говорить() {
    super.speak ();
    console.log (`$ {this.name} roars.`);
  }
}

пусть l = новый Лев ('Нечеткий');
l.speak ();


  

Абстрактные подклассы или примеси - это шаблоны для классов. Класс ECMAScript может иметь только один суперкласс, поэтому множественное наследование от классов инструментов, например, невозможно. Функциональность должна предоставляться суперклассом.

Функция с суперклассом в качестве входных данных и подклассом, расширяющим этот суперкласс в качестве выходных данных, может использоваться для реализации микширования в ECMAScript:

  let CalculatorMixin = Base => class extends Base {
  calc () {}
};

let randomizerMixin = Base => class extends Base {
  randomize () {}
};
  

Класс, который использует эти микшеры, можно записать так:

  класс Foo {}
class Bar расширяет CalculatorMixin (randomizerMixin (Foo)) {}
  

Класс не может быть переопределен.Попытка сделать это приводит к ошибке SyntaxError .

Если вы экспериментируете с кодом в веб-браузере, например в веб-консоли Firefox ( Tools > Web Developer > Web Console ), и вы дважды выполняете определение класса с тем же именем, вы получите SyntaxError: повторное объявление let ClassName; . (См. Дальнейшее обсуждение этой проблемы в ошибке 1428672.) Выполнение чего-то подобного в Инструментах разработчика Chrome дает вам сообщение типа Uncaught SyntaxError: идентификатор ClassName уже был объявлен в : 1: 1 .

Таблицы BCD загружаются только в браузере

конструктор - JavaScript | MDN

Метод конструктора - это специальный метод класс для создания и инициализации объекта этот класс.

  конструктор () {...}
конструктор (аргумент0) {...}
конструктор (аргумент0, аргумент1) {...}
конструктор (аргумент0, аргумент1, ..., аргументN) {...}
  

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

  class Person {

  конструктор (имя) {
    this.name = имя;
  }

  представлять() {
    console.log (`Привет, меня зовут $ {this.name}`);
  }

}

const otto = новый человек ('Отто');

otto.introduce ();
  

Если вы не предоставите свой собственный конструктор, будет предоставлен конструктор по умолчанию. для тебя. Если ваш класс является базовым, конструктор по умолчанию пуст:

Если ваш класс является производным классом, конструктор по умолчанию вызывает родительский конструктор, передача любых аргументов, которые были предоставлены:

  конструктор (...args) {
  супер (... аргументы);
}
  

Это позволяет работать с таким кодом:

  class ValidationError расширяет Error {

  printCustomerMessage () {
    return `Ошибка проверки :-( (подробности: $ {this.message})`;
  }

}

пытаться {
  throw new ValidationError («Недействительный номер телефона»);
} catch (ошибка) {
   if (error instanceof ValidationError) {
    console.log (error.name);
    console.log (error.printCustomerMessage ());
  } еще {
    console.log ('Неизвестная ошибка', ошибка);
    выбросить ошибку;
  }
}
  

Класс ValidationError не требует явного конструктора, потому что он не требует специальной инициализации.Затем конструктор по умолчанию позаботится о инициализация родительского Ошибка из заданного аргумента.

Однако, если вы предоставляете свой собственный конструктор, и ваш класс является производным от некоторого родительского class, то вы должны явно вызвать конструктор родительского класса, используя супер . Например:

  class ValidationError расширяет Error {

  конструктор (сообщение) {
    супер (сообщение);
    this.name = 'ValidationError';
    this.code = '42';
  }

  printCustomerMessage () {
     return `Ошибка проверки :-( (подробности: $ {this.message}, код: $ {this.code}) `;
  }

}

пытаться {
  throw new ValidationError («Недействительный номер телефона»);
} catch (ошибка) {
   if (error instanceof ValidationError) {
    console.log (error.name);
    console.log (error.printCustomerMessage ());
  } еще {
    console.log ('Неизвестная ошибка', ошибка);
    выбросить ошибку;
  }
}
  

В объекте может быть только один специальный метод с именем « constructor ». класс. Наличие более одного экземпляра метода конструктора в классе выдаст ошибку SyntaxError .

Использование конструктора

Метод

Этот фрагмент кода взят из классов образец (живая демонстрация).

  class Square extends Polygon {
  constructor (length) {
    
    
    супер (длина, длина);
    
    
    this.name = 'Квадрат';
  }

  get area () {
    вернуть this.height * this.width;
  }

  установить область (значение) {
    this.height = значение ** 0,5;
    this.width = значение ** 0,5;
  }
}
  

Другой пример

Здесь прототип класса Square изменен, но конструктор его базовый класс Многоугольник по-прежнему вызывается, когда создается новый экземпляр квадрата. созданный.

  class Polygon {
    constructor () {
        this.name = "Многоугольник";
    }
}

class Square extends Polygon {
    constructor () {
        супер();
    }
}

class Rectangle {}

Object.setPrototypeOf (Square.prototype, Rectangle.prototype);

console.log (Object.getPrototypeOf (Square.prototype) === Polygon.prototype);
console.log (Object.getPrototypeOf (Square.prototype) === Rectangle.prototype);

let newInstance = new Square ();
console.log (newInstance.name);
  

Таблицы BCD загружаются только в браузере

Выражение класса - JavaScript | MDN

Выражение класса - это один из способов определить класс в ECMAScript 2015.Подобно функции выражения, выражения класса могут быть именованными или безымянными. Если названо, то имя класса является локальным только для тела класса.

классов JavaScript используют наследование на основе прототипов.

  const MyClass = class [className] [extends otherClassName] {
  
}
  

Выражение класса имеет синтаксис, аналогичный классу декларация (заявление). Как и в случае с операторами класса , тело выражения класса выполняется в строгом режиме.

Есть несколько различий между выражениями классов и операторы класса, однако:

  • Выражения класса могут опускать имя класса («идентификатор привязки»), которое не является возможно с операторами класса.
  • Выражения классов позволяют переопределить (повторно объявить) классы без выброс и SyntaxError . Это не в случае с операторами класса.

Метод конструктора является необязательным.Классы, созданные с помощью class выражения всегда будут отвечать на типа с значение « функция ».

  «использовать строгое»;
let Foo = class {};
Foo = class {};

typeof Foo;
typeof class {};

Foo instanceof Object;
Foo instanceof Function;
class Foo {}
  

Простое выражение класса

Это простое выражение анонимного класса, на которое вы можете ссылаться, используя переменная Foo .

  const Foo = class {
  конструктор () {}
  бар() {
    return 'Hello World!';
  }
};

const instance = new Foo ();
instance.bar ();
Foo.name;
  

Выражения именованных классов

Если вы хотите сослаться на текущий класс внутри тела класса, вы можете создать именованное выражение класса . Имя видно только в рамках класса самовыражение.

  const Foo = class NamedFoo {
  конструктор () {}
  кто там() {
    вернуть NamedFoo.имя;
  }
}
const bar = new Foo ();
bar.whoIsThere ();
NamedFoo.name;
Foo.name;
  

Таблицы BCD загружаются только в браузере

статические - JavaScript | MDN

Ключевое слово static определяет статический метод или свойство для класса. Ни статических методов, ни статических свойств можно вызывать в экземплярах класса. Вместо этого их вызывают в класс сам.

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

Обратите внимание, что в примерах в этой статье используются поля общедоступных классов (включая статические поля общедоступных классов), которые еще не являются частью спецификации ECMAScript, но вместо этого указаны в предложении полей общедоступных и частных экземпляров на TC39.

  static methodName () {...}
static propertyName [= значение];
  

Использование статических членов в классах

Следующий пример демонстрирует несколько вещей:

  1. Как статический член (метод или свойство) определяется в классе.
  2. Что класс со статическим членом может быть подклассом.
  3. Как можно и нельзя вызывать статический член.
  класс Тройной {
  static customName = 'Tripler';
  static description = 'Я утрою любое указанное вами число';
  статический расчет (n = 1) {
    вернуть n * 3;
  }
}

class SquaredTriple расширяет Triple {
  static longDescription;
  static description = 'Я возведу в квадрат тройное любое указанное вами число';
  static calculate (n) {
    вернуть super.calculate (n) * super.вычислить (п);
  }
}

console.log (Triple.description);
console.log (Triple.calculate ());
console.log (Triple.calculate (6));

const tp = новый тройной ();

console.log (SquaredTriple.calculate (3));
console.log (SquaredTriple.description);
console.log (SquaredTriple.longDescription);
console.log (SquaredTriple.customName);


console.log (tp.calculate ());
  

Вызов статических элементов из другого статического метода

Чтобы вызвать статический метод или свойство в другом статическом методе того же класс, вы можете использовать это ключевое слово.

  class StaticMethodCall {
  static staticProperty = 'статическое свойство';
  static staticMethod () {
    return 'Статический метод и' + this.staticProperty + 'был вызван';
  }
  static anotherStaticMethod () {
    return this.staticMethod () + 'из другого статического метода';
  }
}
StaticMethodCall.staticMethod ();


StaticMethodCall.anotherStaticMethod ();

  

Вызов статических членов из конструктора класса и других методов

Статические члены недоступны напрямую с помощью ключевого слова this из нестатические методы.Их нужно вызывать по имени класса: ИМЯ КЛАССА.STATIC_METHOD_NAME () / CLASSNAME.STATIC_PROPERTY_NAME или вызывая метод как свойство конструктор : this.constructor.STATIC_METHOD_NAME () / this.constructor.STATIC_PROPERTY_NAME

  class StaticMethodCall {
  constructor () {
    console.log (StaticMethodCall.staticProperty);
    console.log (this.constructor.staticProperty);
    консоль.журнал (StaticMethodCall.staticMethod ());
    console.log (this.constructor.staticMethod ());
  }

  static staticProperty = 'статическое свойство';
  static staticMethod () {
    return 'статический метод был вызван.';
  }
}
  

Таблицы BCD загружаются только в браузере

Как работает JavaScript: статические блоки класса + 6 предлагаемых семантик | Лоуренс Иглз | Сентябрь 2021 г.

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

Статические блоки класса

в настоящее время являются предложением этапа 3 процесса предложения TC39.

Статические блоки класса предоставляют новый способ выполнения статической инициализации в классе JavaScript. Они не предназначены для замены полей класса, но расширяют возможности использования объявлений полей класса.

Статические блоки класса не новы в программировании. Они использовались в таких языках, как Java и C #, которые используют классическое наследование. В Java они называются статическими инициализаторами, а в C # - статическими конструкторами .

Поскольку JavaScript использует прототипное наследование, в JavaScript не было класса , пока он не был добавлен в ES6.

Классы ES6 позволяют имитировать классическое наследование классов. Следовательно, в язык JavaScript теперь могут быть добавлены такие функции, как статические блоки классов.

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

Чтобы понять статические блоки классов, нам нужно хорошо разбираться в классах JavaScript.А для этого требуется знание объектно-ориентированных - ООП, концепций JavaScript, таких как прототипный шаблон наследования.

Итак, в этой статье мы заложим основу, изучив некоторые важные концепции ООП JavaScript, такие как прототипный шаблон наследования, классы ES6 и объявление полей класса. Затем, опираясь на наши знания, мы узнаем о статических блоках класса.

Давайте начнем с OOP JavaScript в следующем разделе.

Термин наследование относится к процедуре, в которой один объект получает доступ к свойствам и методам другого объекта.

Общая идея наследования реализована по-разному в разных языках. И классическое, и прототипное наследование - это два разных способа совместного использования свойств и методов между объектами.

Классическое наследование - самый популярный метод реализации наследования. Такой шаблон используют такие языки, как Java и C #.

JavaScript, однако, реализует наследование с использованием прототипного шаблона наследования.

Паттерн прототипного наследования намного проще, гибок и легче расширяется. В нем задействована концепция прототипа.

Все объекты в JavaScript имеют свойство prototype, которое ссылается на объект proto в памяти. И свойство proto объекта является прототипом этого объекта.

Рассмотрим код ниже:

В приведенном выше коде мы создали объект john, используя Object.create () метод JavaScript, который позволяет нам создать новый объект, используя существующий объект в качестве его прототипа.

Таким образом, в приведенном выше коде объект person является прототипом объекта john , а john .__ proto__ - объектом person .

Кроме того, прототип может иметь свой собственный прототип, и когда движку JavaScript не удается найти свойство или метод в объекте, он будет искать его в своем прототипе и продолжать поиск по цепочке прототипов. Он возвращает свойство или метод, если находит его, или undefined, если не найден.

Рассмотрим код ниже:

В нашем коде выше, установив john в качестве прототипа объекта johnson , мы можем получить доступ к свойству isHuman из объекта johnson . Даже если свойство isHuman находится в объекте person , который является прототипом john . Это просто потому, что движок JavaScript ищет это свойство в цепочке прототипов.

Цепочку прототипов не следует путать с цепочкой областей видимости, хотя они похожи.Цепочка областей видимости предназначена для поиска того, где у нас есть доступ к переменной, а цепочка прототипов - это то, где у нас есть доступ к свойству или методу среди последовательности объектов, связанных через свойство прототипа.

Кроме того, два объекта могут совместно использовать один и тот же прототип, и это обеспечивает отличный способ совместного использования свойств и методов между объектами. Мы узнаем об этом больше в следующем подразделе.

Объявления CustomType

В ES5 не было классов.Самым близким к классу является метод, называемый созданием настраиваемого типа. Он включает в себя создание конструктора и назначение методов прототипу конструктора, как показано ниже:

В приведенном выше коде PersonFn - это конструктор, который создает единственное свойство с именем name.

И мы разделяем метод sayName со всеми экземплярами конструктора PersonFn , добавляя его к прототипу конструктора PersonFn .

Это потому, что экземпляр конструктора будет иметь тот же прототип, что и прототип конструктора.Таким образом, в нашем коде выше оба объекта malePerson и femalePerson имеют доступ к методу sayName через прототипное наследование, поскольку они оба являются экземплярами конструктора PersonFn .

Объявление класса ES6

Рассмотрим код ниже:

Приведенное выше объявление PersonClass ведет себя аналогично конструктору PersonFn .

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

Результат такой же, как и у конструктора PersonFn . Но почему классы?

Несмотря на сходство между классами и объявлением настраиваемого типа, ниже приведены некоторые преимущества использования классов:

  1. Классы дают нам более чистый и лаконичный синтаксис
  2. Весь код в объявлении класса выполняется в строгом режиме автоматически, и нет возможности отказ от строгого режима внутри класса.
  3. Объявления классов, в отличие от объявлений функций, не поднимаются.

В отличие от конструктора функции, вызов конструктора класса без оператора new вызывает ошибку.

Как мы видели выше, классы ES6 - это просто синтаксический сахар поверх существующих объявлений пользовательских типов. В этом разделе мы более подробно рассмотрим классы.

Класс JavaScript - это особая функция. И так же, как у нас есть объявление функции и выражение функции, мы также можем иметь объявление класса и выражение класса.

Мы уже видели объявление класса в предыдущем разделе.

Выражение класса может иметь имя или имя, как показано ниже:

Рассмотрим код ниже:

Конструктор класса

Конструктор класса - это специальный метод, который инициализирует экземпляры класса. Это место, где мы устанавливаем начальные значения полей класса.

Рассмотрим код ниже:

В приведенном выше PersonClass конструктор имеет один параметр с именем name , который используется для установки начального значения поля name .Кроме того, в приведенном выше примере свойство name является собственным свойством .

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

Поля класса и предложение объявления поля класса

Поля класса - это свойства класса. Это переменные, содержащие информацию. И это могут быть:

  • Поля экземпляра - поля, которые присутствуют в каждом созданном экземпляре класса.
  • Статические поля: статические поля объявляются с помощью ключевого слова static. Когда мы объявляем поле с помощью ключевого слова static, доступ к полю должен осуществляться напрямую из класса. И к нему нельзя получить доступ из экземпляра класса.

Кроме того, поля класса имеют два уровня доступности:

  • Public - это поле доступно где угодно, в том числе за пределами класса
  • Private - это поле доступно только в объявлении класса

Давайте узнаем об этом подробнее .

Поля открытого класса

Рассмотрим код ниже:

В приведенном выше коде конструктор PersonClass инициализирует поле экземпляра с именем name . Затем мы получили доступ к name из экземпляра класса - к классу malePerson , используя метод доступа к свойству. Таким образом, name является публичным полем.

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

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

Рассмотрим код ниже:

В приведенном выше коде мы видим, что публичное поле экземпляра - name и метод - sayName доступны из экземпляра класса. Однако к общедоступному статическому полю - PERSON_ROLE можно было получить доступ только из класса с помощью Person.ЛИЧНЫЙ_РОЛЬ .

Общедоступные статические поля могут быть полезны для реализации кешей или фиксированных конфигураций. Кроме того, их можно использовать для любых данных, которые вы не хотите реплицировать между экземплярами.

Еще одно преимущество использования вышеприведенного шаблона состоит в том, что методы экземпляра, такие как sayName , могут быть определены как поле класса и назначены с помощью стрелочной функции ES6 => , которая автоматически привязывается к определяющему объекту.

Следовательно, больше нет необходимости добавлять объявления привязки с помощью bind () , как показано в предыдущем примере.

Важно привязать к этому , чтобы предотвратить потерю контекста. Вы можете узнать все о и этой переменной в нашей предыдущей статье этой серии. Кроме того, вы можете получить глубокие знания о методе bind , прочитав нашу статью о вызове, применении и привязке.

Поля класса по умолчанию являются общедоступными, но мы можем создавать частные поля и методы, используя хэш-префикс # , как показано ниже:

В приведенном выше коде мы видим, что # имя - это частное поле класса, которое мы может получить доступ только в объявлении класса с помощью частного метода #getName .

Поскольку метод экземпляра sayName вызывает закрытый метод #getName , который обращается к #name в объявлении класса, мы получаем John Doe , когда вызываем john.sayName ().

Кроме того, мы можем получить доступ только к частному статическому полю - #PERSON_ROLE , используя Person. # PERSON_ROLE в объявлении класса.

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

Разработчики JavaScript часто используют такие соглашения, как _FIELDNAME , слабые карты, закрытие или символы для обозначения частных свойств, но с частными полями класса мы можем просто использовать префикс # .

Статические блоки класса

предназначены для предоставления новых вариантов использования, не обрабатываемых общедоступными и частными полями класса, а также для расширения существующих вариантов использования. Давайте узнаем о них в следующем разделе.

В предыдущем разделе мы узнали, что можем создать статический член класса, добавив к этому члену префикс статического ключевого слова.Кроме того, чтобы создать статический блок , мы префикс блока кода {} в объявлении класса с помощью ключевого слова static, как показано ниже:

Почему статические блоки класса

Статические частные и общедоступные поля могут только включать нам, чтобы выполнить инициализацию статических членов для каждого поля во время оценки определения класса. В случаях, когда нам нужно оценить такой оператор, как try… catch во время инициализации или установить несколько статических полей с одним и тем же значением, нам придется написать эту логику вне класса.

Рассмотрим код ниже:

Приведенный выше код вызывает ошибку. Однако мы можем реорганизовать его, используя статический блок класса, как показано ниже:

https://drive.google.com/drive/u/1/folders/1E0IwaWIpE349JUkz8G_ZTRNGrlIthKqs

Как видно выше, с помощью статических блоков класса мы можем оценить попытку . … Catch в объявлении класса.

Кроме того, статические блоки класса предоставляют привилегированный доступ к закрытым полям и методам с лексической областью видимости.

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

Рассмотрим код ниже:

В приведенном выше коде мы смогли совместно использовать свойство частного экземпляра класса Person с функцией readPrivateData .

Это интересно, потому что, как отмечалось выше, свойство частного экземпляра доступно только в теле класса, который его определяет.

Предлагаемая семантика

Ниже представлена ​​предлагаемая семантика статических блоков класса:

  • Блок статической инициализации создает новую лексическую область видимости, которая вложена в лексическую область тела класса, предоставляя привилегированный доступ к частным свойствам экземпляра для класс.
  • Класс может иметь несколько статических блоков класса.
  • Хотя поля класса являются допустимой целью для декораторов, статические блоки класса декорировать нельзя. Однако вы можете украсить сам класс. Если вы новичок в декораторах JavaScript, вы можете узнать о них больше, прочитав нашу предыдущую статью о декораторах в этой серии.
  • При оценке эта переменная в статическом блоке инициализации является объектом-конструктором.
  • Это синтаксическая ошибка для вызова super - super () в статическом блоке инициализации.
  • Ссылка на аргументы из статического блока инициализации {} является синтаксической ошибкой.

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

В качестве предложения этапа 3 вы можете воспользоваться этой функцией, используя такие компиляторы, как babel. И после изучения этого руководства вы обязательно должны быть готовы попробовать.

Каждый новый уровень абстракции, делающий ваш код более читаемым и пригодным для повторного использования, потенциально может привести к большим трудностям при поиске и устранении неисправностей. Поскольку качество кода и расширяемость критически важны для каждого проекта, вы можете принять решение о добавлении дополнительных уровней абстракции и компенсировать риск, добавив более совершенные решения для мониторинга и аналитики.

Такое решение, как SessionStack, сделает процесс отладки намного более эффективным, поскольку оно позволяет воспроизводить путешествия клиента в виде видео.Это позволяет вам видеть, как клиенты взаимодействовали с вашим веб-приложением и что происходило на их экране при возникновении проблемы. Объединение этой визуальной информации со всеми техническими сведениями о браузере, устройстве, сети и среде предоставит вам весь контекст, необходимый для воспроизведения проблемы и ее эффективного решения.

Существует бесплатная пробная версия, если вы хотите попробовать SessionStack.

SessionStack, воспроизводящий сеанс

Если вы пропустили предыдущие главы серии, вы можете найти их здесь:

Новейшие классы JavaScript

В этой статье мы рассмотрим новейшие классы JavaScript.

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

В

JavaScript изначально не было классов. Классы были добавлены с введением ECMASCRIPT 6 (es6), новой и улучшенной версии JavaScript (ECMASCRIPT 5 - более старая версия).

Типичный класс JavaScript - это объект с методом конструктора по умолчанию. Классы JavaScript построены на прототипах, но с синтаксисом выражений.

До того, как классы появились, прототипы использовались для имитации классов в JavaScript. Прототип - это объект по умолчанию, прикрепленный к каждой функции и объекту JavaScript. К прототипу могут быть прикреплены дополнительные свойства, что помогает нам имитировать классы JavaScript.

Чтобы лучше понять это, давайте объявим функцию с именем «car» с двумя параметрами: возрастом и именем.

 function Car () {
    this.name = 'дракон';
    this.age = 3;
}
 

Дополнительное свойство можно добавить с помощью прототипов, как вы увидите в кодовом блоке ниже:

 Автомобиль.prototype.color = 'белый'
 

Теперь давайте создадим новый экземпляр автомобиля:

.
 пусть Car1 = новый автомобиль ()
 

Теперь мы собираемся записать в консоль только что добавленное свойство:

 console.log (Car1.color)
 

Функциональная машина JavaScript в данном случае служит классом с тремя свойствами: именем, возрастом и цветом. Другими словами, JavaScript использует наследование, которое поставляется с прототипами для имитации классов.

Классы ES6

С введением классов в JavaScript ES6 предоставил нам гораздо более лаконичный способ объявления классов с использованием синтаксиса, аналогичного синтаксису других объектно-ориентированных языков программирования.В отличие от подхода ES5 к классам, ES6 не нуждается в ключевом слове function при работе с классами, хотя и скрытно. JavaScript по-прежнему считает классы особым типом функций.

Возможно, основное различие между классом и функцией заключается в подъеме: в отличие от функций, классы JavaScript должны быть объявлены перед доступом. В противном случае выдаст ошибку.

Ключевое слово класса

JavaScript предоставляет нам ключевое слово class, которое является основным способом определения классов.Он служит синтаксическим сахаром к уже существующему шаблону наследования прототипов.

 // объявление класса javascript
class Car {
   // методы
}
 

Как показано выше, ключевое слово class используется для указания того, что класс JavaScript определяется.

При определении класса можно следовать подходу, отличному от описанного выше, с помощью выражений класса для большей гибкости. Таким образом, класс может быть назван или безымянным.

 // безымянное выражение класса javascript
let Car = class {
    конструктор (имя, возраст) {
        это.name = имя
        this.age = возраст
    }
}
 

Вот пример именованного выражения класса JavaScript:

 // именованное выражение класса javascript
let Car = class Car {
    конструктор (имя, возраст) {
        this.name = имя
        this.age = возраст
    }
}
 

Метод конструктора

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

Если не определено, JavaScript добавляет к рассматриваемому классу пустой конструктор с нулевым параметром.

Вот пример класса с методом конструктора:

 // класс javascript с конструктором
 class Car {
    конструктор (имя, возраст) {
        this.name = имя
        this.age = возраст
    }
}
 

Вышеупомянутый класс содержит конструктор с двумя параметрами: имя и возраст.

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

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

Вот типичный пример статического метода:

 class Math {
    static add (number1, number2) {
        вернуть число1 + число2
    }
}
 

Вышеупомянутый статический метод затем можно вызвать, как показано ниже:

 пусть addResult = Math.add (2,3)
 

Обратите внимание, что статический метод add показывает нам, что означает добавление.

Синтаксис класса ES6

Типичный класс JavaScript имеет синтаксис, показанный ниже:

Автомобиль класса
 {
    конструктор(){
        // операция по умолчанию
    }
    method () {
        // операция2
    }

}
 

Проблемы с классами ES6

Классы

могут предоставить более сложное решение для более простого способа выполнения определенных операций в JavaScript.Люди, имеющие опыт работы с объектно-ориентированным языком программирования, могут решить выполнять простые операции с классами, даже если в них нет необходимости.

Некоторые разработчики могут возразить, что добавление классов лишило JavaScript оригинальности и что использование прототипов было более гибким способом выполнения операций, требующих классов. Это связано с тем, что в отличие от классов в других объектно-ориентированных языках программирования, JavaScript не предоставляет базовых функций класса, таких как объявление частных переменных.

ECMASCRIPT 2020 направлен на решение некоторых из этих проблем.

ECMASCRIPT 2020 дополнения к классам

Каждый год в JavaScript вносятся дополнения и изменения для удовлетворения потребностей пользователей JavaScript. Последние изменения находятся в ECMASCRIPT 2020. Некоторые из дополнений к классам на 2020 год включают частные переменные класса и статические поля.

Переменная частного класса : При работе с большой кодовой базой с большим количеством объявлений переменных может возникнуть необходимость в переменной, доступ к которой можно получить только внутри класса.Переменная частного класса решает эту проблему. Добавив хэш перед именем переменной, можно легко создать частную переменную.

 class Detail {
      #name = "steve"
     Добро пожаловать(){
        console.log (это сообщение #)
      }
 }

 const detail_1 = новая деталь ()
   detail_1.welcome ()
 

Вышеупомянутая переменная «#name» доступна только в классе «Detail».

Статические поля : Для понимания статических полей рассмотрим приведенный ниже фрагмент кода;

 class Detail {
     #name = "Стивен"

     Добро пожаловать(){
         консоль.журнал (это. # сообщение)
     }
 }
 

В более старой версии JavaScript попытка получить доступ к методу «welcome» без создания нового экземпляра класса кажется невозможной. Но с последним дополнением мы можем получить доступ к этим методам без необходимости создания экземпляра.

Доступ к вышеуказанному методу можно получить, как показано ниже:

 const DetailMethod = Detail.welcome ()
 

Заключение

Классы

JavaScript решают некоторые проблемы, возникающие при использовании прототипов.Они делают объявление класса более прямым и понятным. Новейший ECMASCRIPT 2020 упрощает использование классов, добавляя больше гибкости.

LogRocket: упрощает отладку ошибок JavaScript за счет понимания контекста

Отладка кода - всегда утомительная задача. Но чем больше вы понимаете свои ошибки, тем легче их исправить.

LogRocket позволяет вам понять эти ошибки новыми и уникальными способами. Наше решение для мониторинга внешнего интерфейса отслеживает взаимодействие пользователей с вашими интерфейсами JavaScript, чтобы дать вам возможность точно узнать, что именно сделал пользователь, приведший к ошибке.

LogRocket записывает журналы консоли, время загрузки страницы, трассировки стека, медленные сетевые запросы / ответы с заголовками + телами, метаданными браузера и настраиваемыми журналами. Понять влияние вашего кода JavaScript никогда не будет так просто!

Попробуйте бесплатно.

Что не так с классами в JavaScript? | Фернандо Доглио

Если наша текущая модель ООП настолько тонка и представляет собой лишь слой абстракции над прототипным наследованием, что именно нам не хватает? Что могло сделать JS действительно ООП?

Хороший способ взглянуть на этот вопрос - просто посмотреть, что делает TypeScript.Команда, стоящая за этим языком, определенно подталкивает JavaScript к пределам, создавая что-то, что можно перевести на JS. Это, в свою очередь, также ограничивает их возможности, но хороший способ начать наш список желаний ООП - это посмотреть на их функции, связанные с ООП:

Есть одно предостережение, которое вы заметите через секунду: некоторые из отсутствующих Конструкции ООП в JavaScript прямо сейчас имеют встроенную функцию проверки типов, которая не имеет реального значения внутри динамически типизированного языка, поэтому, вероятно, поэтому они еще не были добавлены.

Интерфейсы

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

Этого нельзя сделать в простом JS.

Вы определенно можете добиться того же с помощью класса, определяющего метод speak , а затем его перезаписывающего.Но опять же, вы также можете сделать это на любом другом языке, строго ориентированном на ООП. Интерфейсы чище и элегантнее.

Абстрактные классы

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

Статический полиморфизм

Мне лично понравился этот из моих дней ООП, и иногда мне кажется, что он пригодился бы, если бы он поддерживался и в JS. Статический полиморфизм позволяет нам определять один и тот же метод несколько раз в одном классе, но с разными сигнатурами.

Оставить комментарий

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *