From 074785cea106179cb3305637055ab0a009ca74f2 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:42:52 -0500 Subject: initial commit --- .../reference/classes/class_fields/index.html | 350 +++++++++++++++++ .../reference/classes/constructor/index.html | 205 ++++++++++ .../reference/classes/extends/index.html | 179 +++++++++ .../ru/web/javascript/reference/classes/index.html | 414 +++++++++++++++++++++ .../javascript/reference/classes/static/index.html | 129 +++++++ .../index.html" | 205 ++++++++++ 6 files changed, 1482 insertions(+) create mode 100644 files/ru/web/javascript/reference/classes/class_fields/index.html create mode 100644 files/ru/web/javascript/reference/classes/constructor/index.html create mode 100644 files/ru/web/javascript/reference/classes/extends/index.html create mode 100644 files/ru/web/javascript/reference/classes/index.html create mode 100644 files/ru/web/javascript/reference/classes/static/index.html create mode 100644 "files/ru/web/javascript/reference/classes/\320\277\321\200\320\270\320\262\320\260\321\202\320\275\321\213\320\265_\320\277\320\276\320\273\321\217_\320\272\320\273\320\260\321\201\321\201\320\260/index.html" (limited to 'files/ru/web/javascript/reference/classes') diff --git a/files/ru/web/javascript/reference/classes/class_fields/index.html b/files/ru/web/javascript/reference/classes/class_fields/index.html new file mode 100644 index 0000000000..ade4b8151f --- /dev/null +++ b/files/ru/web/javascript/reference/classes/class_fields/index.html @@ -0,0 +1,350 @@ +--- +title: Поля классов +slug: Web/JavaScript/Reference/Classes/Class_fields +tags: + - JavaScript + - Классы +translation_of: Web/JavaScript/Reference/Classes/Public_class_fields +--- +
{{JsSidebar("Classes")}}
+ +
Объявление публичных и приватных полей является экспериментальной разработкой, предложенной в коммитете стандартов JavaScript TC39. Поддержка браузерами ограничена, но данное нововведение можно использовать посредством транспиляторов (например, Babel). Смотрите информацию о совместимости ниже.
+ +

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

+ +

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

+ +

Публичные статические поля

+ +

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

+ +

Публичные статические поля объявляются при помощи ключевого слова static. Они добавляются в конструктор класса во время его создания с помощью Object.defineProperty. Доступ также осуществляется через конструктор класса.

+ +
class ClassWithStaticField {
+  static staticField = 'static field';
+}
+
+console.log(ClassWithStaticField.staticField);
+// Ожидаемый вывод: "static field"
+
+ +

Поля без инициализации имеют значение undefined.

+ +
class ClassWithStaticField {
+  static staticField;
+}
+
+console.assert(ClassWithStaticField.hasOwnProperty('staticField'));
+console.log(ClassWithStaticField.staticField);
+// Ожидаемый вывод: "undefined"
+ +

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

+ +
class ClassWithStaticField {
+  static baseStaticField = 'base field';
+}
+
+class SubClassWithStaticField extends ClassWithStaticField {
+  static subStaticField = 'sub class field';
+}
+
+console.log(SubClassWithStaticField.subStaticField);
+// Ожидаемый вывод: "sub class field"
+
+console.log(SubClassWithStaticField.baseStaticField);
+// Ожидаемый вывод: "base field"
+ +

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

+ +
class ClassWithStaticField {
+  static baseStaticField = 'base static field';
+  static anotherBaseStaticField = this.baseStaticField;
+
+  static baseStaticMethod() { return 'base static method output'; }
+}
+
+class SubClassWithStaticField extends ClassWithStaticField {
+  static subStaticField = super.baseStaticMethod();
+}
+
+console.log(ClassWithStaticField.anotherBaseStaticField);
+// Ожидаемый вывод: "base static field"
+
+console.log(SubClassWithStaticField.subStaticField);
+// Ожидаемый вывод: "base static method output"
+
+ +

Публичные поля экземпляра

+ +

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

+ +

Публичные поля экземпляра добавляются через Object.defineProperty либо перед тем, как будет исполнено тело конструктора в базовом классе, либо после того, как завершится super() в классе наследнике.

+ +
class ClassWithInstanceField {
+  instanceField = 'instance field';
+}
+
+const instance = new ClassWithInstanceField();
+console.log(instance.instanceField);
+// Ожидаемый вывод: "instance field"
+ +

Поля без инициализации имеют значение undefined.

+ +
class ClassWithInstanceField {
+  instanceField;
+}
+
+const instance = new ClassWithInstanceField();
+console.assert(instance.hasOwnProperty('instanceField'));
+console.log(instance.instanceField);
+// Ожидаемый вывод: "undefined"
+ +

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

+ +
const PREFIX = 'prefix';
+
+class ClassWithComputedFieldName {
+    [`${PREFIX}Field`] = 'prefixed field';
+}
+
+const instance = new ClassWithComputedFieldName();
+console.log(instance.prefixField);
+// Ожидаемый вывод: "prefixed field"
+ +

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

+ +
class ClassWithInstanceField {
+  baseInstanceField = 'base field';
+  anotherBaseInstanceField = this.baseInstanceField;
+  baseInstanceMethod() { return 'base method output'; }
+}
+
+class SubClassWithInstanceField extends ClassWithInstanceField {
+  subInstanceField = super.baseInstanceMethod();
+}
+
+const base = new ClassWithInstanceField();
+const sub = new SubClassWithInstanceField();
+
+console.log(base.anotherBaseInstanceField);
+// Ожидаемый вывод: "base field"
+
+console.log(sub.subInstanceField);
+// Ожидаемый вывод: "base method output"
+ +

Публичные методы

+ +

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

+ +

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

+ +

{{EmbedInteractiveExample("pages/js/classes-static.html")}}

+ + + +

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

+ +

Публичные методы экземпляра

+ +

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

+ +
class ClassWithPublicInstanceMethod {
+  publicMethod() {
+    return 'hello world';
+  }
+}
+
+const instance = new ClassWithPublicInstanceMethod();
+console.log(instance.publicMethod());
+// Ожидаемый вывод: "hello worl​d"
+ +

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

+ +

Вы можете использовать генераторы, асинхронные функции и асинхронные генераторы.

+ +
class ClassWithFancyMethods {
+  *generatorMethod() { }
+  async asyncMethod() { }
+  async *asyncGeneratorMethod() { }
+}
+ +

Внутри методов экземпляра, this ссылается на сам экземпляр.
+ В классах наследниках, super дает доступ к прототипу базового класса, позволяя вызывать его методы.

+ +
class BaseClass {
+  msg = 'hello world';
+  basePublicMethod() {
+    return this.msg;
+  }
+}
+
+class SubClass extends BaseClass {
+  subPublicMethod() {
+    return super.basePublicMethod();
+  }
+}
+
+const instance = new SubClass();
+console.log(instance.subPublicMethod());
+// Ожидаемый вывод: "hello worl​d"
+
+ +

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

+ +
class ClassWithGetSet {
+  #msg = 'hello world';
+  get msg() {
+    return this.#msg;
+  }
+  set msg(x) {
+    this.#msg = `hello ${x}`;
+  }
+}
+
+const instance = new ClassWithGetSet();
+console.log(instance.msg);
+// Ожидаемый вывод: "hello worl​d"
+
+instance.msg = 'cake';
+console.log(instance.msg);
+// Ожидаемый вывод: "hello cake"
+
+ +

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

+ +

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

+ +

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

+ +

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

+ +
class ClassWithPrivateStaticField {
+  static #PRIVATE_STATIC_FIELD;
+
+  static publicStaticMethod() {
+    ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD = 42;
+    return ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD;
+  }
+}
+
+assert(ClassWithPrivateStaticField.publicStaticMethod() === 42);
+ +

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

+ +

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

+ +
class BaseClassWithPrivateStaticField {
+  static #PRIVATE_STATIC_FIELD;
+
+  static basePublicStaticMethod() {
+    this.#PRIVATE_STATIC_FIELD = 42;
+    return this.#PRIVATE_STATIC_FIELD;
+  }
+}
+
+class SubClass extends BaseClassWithPrivateStaticField { }
+
+assertThrows(() => SubClass.basePublicStaticMethod(), TypeError);
+
+ +

Приватные поля экземпляра объекта

+ +

Приватные поля объекта объявляются как # names ( произносятся как "hash names"), которые являются идентификаторами с префиксом #.  # является частью самого имени и также используется для объявления и доступа.

+ +

Инкапсуляция обеспечивается языком. Ссылка на # names вне области видимости является синтаксической ошибкой.

+ +
class ClassWithPrivateField {
+  #privateField;
+
+  constructor() {
+    this.#privateField = 42;
+    this.#randomField = 666; # Syntax error
+  }
+}
+
+const instance = new ClassWithPrivateField();
+instance.#privateField === 42; // Syntax error
+
+ +

Приватные методы

+ +

Приватные статические методы

+ +

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

+ +

Приватные статические методы могут быть генераторами, асинхронными функциями и асинхронными генераторами.

+ +
class ClassWithPrivateStaticMethod {
+    static #privateStaticMethod() {
+        return 42;
+    }
+
+    static publicStaticMethod() {
+        return ClassWithPrivateStaticMethod.#privateStaticMethod();
+    }
+}
+
+assert(ClassWithPrivateStaticField.publicStaticMethod() === 42);
+
+ +

Приватные методы экземпляра объекта

+ +

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

+ +
class ClassWithPrivateMethod {
+  #privateMethod() {
+    return 'hello world';
+  }
+
+  getPrivateMessage() {
+      return #privateMethod();
+  }
+}
+
+const instance = new ClassWithPrivateMethod();
+console.log(instance.getPrivateMessage());
+// expected output: "hello worl​d"
+ +

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

+ +
class ClassWithPrivateAccessor {
+  #message;
+
+  get #decoratedMessage() {
+    return `✨${this.#message}✨`;
+  }
+  set #decoratedMessage(msg) {
+    this.#message = msg;
+  }
+
+  constructor() {
+    this.#decoratedMessage = 'hello world';
+    console.log(this.#decoratedMessage);
+  }
+}
+
+new ClassWithPrivateAccessor();
+// expected output: "✨hello worl​d✨"
+
+ +

Совместимость с браузерами

+ +

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

+ + + +

{{Compat("javascript.classes.public_class_fields")}}

+ +

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

+ + + +

{{Compat("javascript.classes.private_class_fields")}}

+ +

Смотрите также

+ + diff --git a/files/ru/web/javascript/reference/classes/constructor/index.html b/files/ru/web/javascript/reference/classes/constructor/index.html new file mode 100644 index 0000000000..7d7fe14e82 --- /dev/null +++ b/files/ru/web/javascript/reference/classes/constructor/index.html @@ -0,0 +1,205 @@ +--- +title: constructor +slug: Web/JavaScript/Reference/Classes/constructor +tags: + - ECMAScript 2015 + - ES6 + - JavaScript + - Классы +translation_of: Web/JavaScript/Reference/Classes/constructor +--- +
{{jsSidebar("Classes")}}
+ +
constructor - специальный метод, служащий для создания и инициализации объектов, созданных с использованием class.
+ +

Синтаксис

+ +
constructor([arguments]) { ... }
+ +

Описание

+ +

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

+ +
class Person {
+
+  constructor(name) {
+    this.name = name;
+  }
+
+  introduce() {
+    console.log(`Hello, my name is ${this.name}`);
+  }
+
+}
+
+const otto = new Person('Отто');
+
+otto.introduce();
+ +

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

+ +
constructor() {}
+ +

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

+ +
constructor(...args) {
+  super(...args);
+}
+ +

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

+ +
class ValidationError extends Error {
+
+  printCustomerMessage() {
+    return `Проверка не удалась :-( (подробности: ${this.message})`;
+  }
+
+}
+
+try {
+  throw new ValidationError("Не правильный номер телефона");
+} catch (error) {
+   if (error instanceof ValidationError) {
+    console.log(error.name); // Это Error вместо ValidationError!
+    console.log(error.printCustomerMessage());
+  } else {
+    console.log('Не известная ошибка', error);
+    throw error;
+  }
+}
+ +

ValidationError классу не нужен явный (explicit) конструктор, потому что не требуется инициализация. Затем конструктор по умолчанию позаботится о инициализации родительского класса Error переданным ему аргументом.

+ +

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

+ +
class ValidationError extends Error {
+
+  constructor(message) {
+    super(message);  // вызов конструктора родительского класса
+    this.name = 'ValidationError';
+    this.code = '42';
+  }
+
+  printCustomerMessage() {
+     return `Проверка не удалась :-( (подробности: ${this.message}, code: ${this.code})`;
+  }
+
+}
+
+try {
+  throw new ValidationError("Не правильный номер телефона");
+} catch (error) {
+   if (error instanceof ValidationError) {
+    console.log(error.name); // Теперь это ValidationError!
+    console.log(error.printCustomerMessage());
+  } else {
+    console.log('Не известная ошибка', error);
+    throw error;
+  }
+}
+ +

В классе может быть только один метод с именем "constructor". Если класс содержит более одного constructor, будет сгенерировано исключение {{jsxref("SyntaxError")}}.

+ +

Примеры

+ +

Использование метода constructor

+ +

Данный фрагмент кода взят из classes sample (live demo).

+ +
class Square extends Polygon {
+  constructor(length) {
+    // Здесь вызывается конструктор родительского класса,
+    // в который передается length в качестве аргументов,
+    // соответствующим полям width и height класса Polygon
+    super(length, length);
+    // Заметка: В производном классе, super() должен вызываться перед тем как
+    // вы сможете использовать 'this'. Иначе будет сгенерировано исключение reference error.
+    this.name = 'Square';
+  }
+
+  get area() {
+    return this.height * this.width;
+  }
+
+  set area(value) {
+    this.area = value;
+  }
+}
+ +

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

+ +

Посмотрите на этот отрывок кода.

+ +
class Polygon {
+  constructor() {
+    this.name = "Polygon";
+  }
+}
+
+class Square extends Polygon {
+  constructor() {
+    super();
+  }
+}
+
+class Rectangle {}
+
+Object.setPrototypeOf(Square.prototype, Rectangle.prototype);
+
+console.log(Object.getPrototypeOf(Square.prototype) === Polygon.prototype); //false
+console.log(Object.getPrototypeOf(Square.prototype) === Rectangle.prototype); //true
+
+let newInstance = new Square();
+console.log(newInstance.name); //Polygon
+ +

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

+ +

Constructors по умолчанию.

+ +

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

+ +
constructor() {}
+ +

Для производных классов, constructor по умолчанию:

+ +
constructor(...args) {
+  super(...args);
+}
+ +

Спецификация

+ + + + + + + + + + + + + + + + + + + +
SpecificationStatusComment
{{SpecName('ES2015', '#sec-static-semantics-constructormethod', 'Constructor Method')}}{{Spec2('ES2015')}}Изначальное определение.
{{SpecName('ESDraft', '#sec-static-semantics-constructormethod', 'Constructor Method')}}{{Spec2('ESDraft')}}
+ +

Совместимость с браузерами

+ + + +

{{Compat("javascript.classes.constructor")}}

+ +

Смотрите также

+ + diff --git a/files/ru/web/javascript/reference/classes/extends/index.html b/files/ru/web/javascript/reference/classes/extends/index.html new file mode 100644 index 0000000000..df642e9e23 --- /dev/null +++ b/files/ru/web/javascript/reference/classes/extends/index.html @@ -0,0 +1,179 @@ +--- +title: extends +slug: Web/JavaScript/Reference/Classes/extends +tags: + - Классы + - Наследование + - Расширение +translation_of: Web/JavaScript/Reference/Classes/extends +--- +
{{jsSidebar("Classes")}}
+ +

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

+ +

Синтаксис

+ +
class ChildClass extends ParentClass { ... }
+ +

Описание

+ +

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

+ +

Свойство .prototype родительского класса или объекта должно быть {{jsxref("Object")}} или {{jsxref("null")}}.

+ +

Примеры

+ +

Использование extends

+ +

В первом примере создаётся дочерний класс с именем Square от класса с именем Polygon. Этот пример был взят из live demo (source).

+ +
class Square extends Polygon {
+  constructor(length) {
+    // Здесь вызывается конструктор родительского класса,
+    // в который передается свойство length в качестве
+    // аргументов, соответствующих полям width и height,
+    // класса Polygon
+    super(length, length);
+    // Примечание:
+    // В конструкторе класса, метод super() должен быть вызван
+    // перед использованием this. В противном случае, будет
+    // выброшена ошибка.
+    this.name = 'Square';
+  }
+
+  get area() {
+    return this.height * this.width;
+  }
+}
+ +

Расширение встроенных объектов с помощью extends

+ +

Этот пример расширяет встроенный объект {{jsxref("Date")}}. Пример взят из live demo (source).

+ +
class myDate extends Date {
+  constructor() {
+    super();
+  }
+
+  getFormattedDate() {
+    var months = [
+      'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+      'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
+    ];
+    return this.getDate() + '-' +
+      months[this.getMonth()] + '-' +
+      this.getFullYear();
+  }
+}
+ +

Расширение null

+ +

Расширение {{jsxref("null")}} работает как и с обычным классом, за исключением того, что прототип объекта не наследует {{jsxref("Object.prototype")}}.

+ +
class nullExtends extends null {
+  constructor() {}
+}
+
+Object.getPrototypeOf(nullExtends); // Function.prototype
+Object.getPrototypeOf(nullExtends.prototype) // null
+
+new nullExtends(); //ReferenceError: this is not defined
+
+ +

Спецификация

+ + + + + + + + + + + + + + + + + + + +
SpecificationStatusComment
{{SpecName('ES2015', '#sec-class-definitions', 'extends')}}{{Spec2('ES2015')}}Initial definition.
{{SpecName('ESDraft', '#sec-class-definitions', 'extends')}}{{Spec2('ESDraft')}} 
+ +

Совместимость с браузерами

+ +

{{CompatibilityTable}}

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari
Basic support{{CompatChrome(42.0)}}{{CompatGeckoDesktop(45)}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
Array subclassing{{CompatChrome(43.0)}}{{CompatNo}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari MobileChrome for Android
Basic support{{CompatNo}}{{CompatGeckoMobile(45)}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatChrome(42.0)}}
Array subclassing{{CompatNo}}{{CompatNo}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatChrome(43.0)}}
+
+ +

Смотрите также

+ + diff --git a/files/ru/web/javascript/reference/classes/index.html b/files/ru/web/javascript/reference/classes/index.html new file mode 100644 index 0000000000..883df1a1be --- /dev/null +++ b/files/ru/web/javascript/reference/classes/index.html @@ -0,0 +1,414 @@ +--- +title: Классы +slug: Web/JavaScript/Reference/Classes +tags: + - ECMAScript 2015 + - ECMAScript6 + - JavaScript + - Reference + - Классы + - Наследование +translation_of: Web/JavaScript/Reference/Classes +--- +
{{JsSidebar("Классы")}}
+ +

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

+ +

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

+ +

На самом деле классы — это "специальные функции", поэтому точно также, как вы определяете функции (function expressions и function declarations), вы можете определять и классы с помощью: class declarations и class expressions.

+ +

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

+ +

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

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

Подъём (hoisting)

+ +

Разница между объявлением функции (function declaration) и объявлением класса (class declaration) в том, что объявление функции совершает подъём ({{Glossary("Hoisting", "hoisted")}}), в то время как объявление класса — нет. Поэтому вначале необходимо объявить ваш класс и только затем работать с ним, а код же вроде следующего сгенерирует исключение типа {{jsxref("ReferenceError")}}:

+ +
var p = new Rectangle(); // ReferenceError
+
+class Rectangle {}
+ +

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

+ +

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

+ +
// безымянный
+var Rectangle = class {
+  constructor(height, width) {
+    this.height = height;
+    this.width = width;
+  }
+};
+console.log(Rectangle.name);
+// отобразится: "Rectangle"
+
+// именованный
+var Rectangle = class Rectangle2 {
+  constructor(height, width) {
+    this.height = height;
+    this.width = width;
+  }
+};
+console.log(Rectangle.name);
+// отобразится: "Rectangle2" 
+
+ +
+

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

+
+ +

Тело класса и задание методов

+ +

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

+ +

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

+ +

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

+ +

Constructor

+ +

Метод constructor — специальный метод, необходимый для создания и инициализации объектов, созданных, с помощью класса. В классе может быть только один метод с именем constructor. Исключение типа {{jsxref("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); // 100
+ +

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

+ +

Ключевое слово static, определяет статический метод или свойства для класса. Статические методы и свойства вызываются без инстанцирования их класса, и не могут быть вызваны у экземпляров (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; //undefined
+p1.distance;    //undefined
+p2.displayName; //undefined
+p2.distance;    //undefined
+
+console.log(Point.displayName);      // "Точка"
+console.log(Point.distance(p1, p2)); // 7.0710678118654755
+ +

Привязка 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(); // объект Animal
+let speak = obj.speak;
+speak(); // undefined
+
+Animal.eat() // класс Animal
+let eat = Animal.eat;
+eat(); // undefined
+ +

Если мы напишем этот же код используя классы основанные на функциях, тогда произойдет автоупаковка основанная на значении 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

+ +

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

+ +
class Animal {
+  constructor(name) {
+    this.name = name;
+  }
+
+  speak() {
+    console.log(`${this.name} издает звук.`);
+  }
+}
+
+class Dog extends Animal {
+  constructor(name) {
+    super(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) объекты. Если вам необходимо создать наследование от обычного объекта, в качестве замены можно использовать {{jsxref("Object.setPrototypeOf()")}}:

+ +
var Animal = {
+  speak() {
+    console.log(`${this.name} издает звук.`);
+  }
+};
+
+class Dog {
+  constructor(name) {
+    this.name = name;
+  }
+}
+
+// Если вы этого не сделаете, вы получите ошибку TypeError при вызове speak.
+Object.setPrototypeOf(Dog.prototype, Animal);
+
+let d = new Dog('Митци');
+d.speak(); // Митци издает звук.
+ +

Species

+ +

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

+ +

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

+ +
class MyArray extends Array {
+  // Изменить species на родительский конструктор 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); // false
+console.log(mapped instanceof Array);   // true
+ +

Обращение к родительскому классу с помощью super

+ +

Ключевое слово 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

+ +

Абстрактные подклассы, или 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)) { }
+ +

Спецификации

+ + + + + + + + + + + + + + + + + + + +
СпецификацияСтатусКомментарий
{{SpecName('ES6', '#sec-class-definitions', 'Class definitions')}}{{Spec2('ES6')}}Изначальное определение.
{{SpecName('ESDraft', '#sec-class-definitions', 'Class definitions')}}{{Spec2('ESDraft')}}
+ +

Совместимость с браузерами

+ +

{{Compat("javascript.classes")}}

+ +

Повторное определение класа

+ +

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

+ +

Если мы запускаете код в веб браузере, к примеру в Firefox Web Console (Tools > Web Developer > Web Console) и вы используете ('Run') определение класса с одним и тем же именем дважды, вы получите SyntaxError: redeclaration of let ClassName;. (Обсуждение по ошибке можно посмотреть в {{Bug(1428672)}}.) Chrome Developer Tools возвращает сообщение типа Uncaught SyntaxError: Identifier 'ClassName' has already been declared at <anonymous>:1:1.

+ +

Смотрите также

+ + diff --git a/files/ru/web/javascript/reference/classes/static/index.html b/files/ru/web/javascript/reference/classes/static/index.html new file mode 100644 index 0000000000..cec6516733 --- /dev/null +++ b/files/ru/web/javascript/reference/classes/static/index.html @@ -0,0 +1,129 @@ +--- +title: static +slug: Web/JavaScript/Reference/Classes/static +translation_of: Web/JavaScript/Reference/Classes/static +--- +
{{jsSidebar("Classes")}}
+ +

Для того, чтобы объявить статический метод класса, необходимо использовать ключевое слово static.

+ +

Синтаксис

+ +
static methodName() { ... }
+ +

Описание

+ +

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

+ +

Вызов статических методов

+ +

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

+ +

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

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

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

+ +

Статические методы недоступны напрямую, используя ключевое слово this из нестатических методов. Вам нужно вызвать их с помощью имени класса: CLASSNAME.STATIC_METHOD_NAME() или вызовом метода как свойства конструктора: this.constructor.STATIC_METHOD_NAME().

+ +
class StaticMethodCall {
+  constructor() {
+    console.log(StaticMethodCall.staticMethod());
+    // 'вызван статический метод.'
+
+    console.log(this.constructor.staticMethod());
+    // 'вызван статический метод.'
+  }
+
+  static staticMethod() {
+    return 'вызван статический метод.';
+  }
+}
+ +

Примеры

+ +

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

+ +
    +
  1. Как статический метод реализуется в классе.
  2. +
  3. Как переопределить статический метод при наследовании.
  4. +
  5. Как можно и как нельзя вызывать статические методы.
  6. +
+ +
class Triple {
+  static triple(n) {
+    if (n === undefined) {
+      n = 1;
+    }
+    return n * 3;
+  }
+}
+
+class BiggerTriple extends Triple {
+  static triple(n) {
+    return super.triple(n) * super.triple(n);
+  }
+}
+
+console.log(Triple.triple());        // 3
+console.log(Triple.triple(6));       // 18
+
+var tp = new Triple();
+
+console.log(BiggerTriple.triple(3));
+// 81 (не затрагивается экземпляром родителя)
+
+console.log(tp.triple());
+// Выведет сообщение, что "tripple" не является
+// функцией ('tp.tripple is not a function').
+
+ +

Спецификации

+ + + + + + + + + + + + + + + + + + + +
СпецификацияСтатусКомментарии
{{SpecName('ES2015', '#sec-class-definitions', 'Class definitions')}}{{Spec2('ES2015')}}Изначальное определение
{{SpecName('ESDraft', '#sec-class-definitions', 'Class definitions')}}{{Spec2('ESDraft')}} 
+ +

Поддержка в браузерах

+ + + +

{{Compat("javascript.classes.static")}}

+ +

Смотрите также

+ + diff --git "a/files/ru/web/javascript/reference/classes/\320\277\321\200\320\270\320\262\320\260\321\202\320\275\321\213\320\265_\320\277\320\276\320\273\321\217_\320\272\320\273\320\260\321\201\321\201\320\260/index.html" "b/files/ru/web/javascript/reference/classes/\320\277\321\200\320\270\320\262\320\260\321\202\320\275\321\213\320\265_\320\277\320\276\320\273\321\217_\320\272\320\273\320\260\321\201\321\201\320\260/index.html" new file mode 100644 index 0000000000..09fe2505e2 --- /dev/null +++ "b/files/ru/web/javascript/reference/classes/\320\277\321\200\320\270\320\262\320\260\321\202\320\275\321\213\320\265_\320\277\320\276\320\273\321\217_\320\272\320\273\320\260\321\201\321\201\320\260/index.html" @@ -0,0 +1,205 @@ +--- +title: Приватные поля класса +slug: Web/JavaScript/Reference/Classes/Приватные_поля_класса +translation_of: Web/JavaScript/Reference/Classes/Private_class_fields +--- +
{{JsSidebar("Classes")}}
+ +

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

+ +

Синтаксис

+ +
class ClassWithPrivateField {
+  #privateField
+}
+
+class ClassWithPrivateMethod {
+  #privateMethod() {
+    return 'hello world'
+ }
+}
+
+class ClassWithPrivateStaticField {
+  static #PRIVATE_STATIC_FIELD
+}
+
+ +

Примеры

+ +

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

+ +

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

+ +

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

+ +
class ClassWithPrivateStaticField {
+  static #PRIVATE_STATIC_FIELD
+
+  static publicStaticMethod() {
+    ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD = 42
+    return ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD
+  }
+}
+
+console.assert(ClassWithPrivateStaticField.publicStaticMethod() === 42)
+ +

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

+ +

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

+ +

Это может привести к неожиданному поведению при использовании this.

+ +
class BaseClassWithPrivateStaticField {
+  static #PRIVATE_STATIC_FIELD
+
+  static basePublicStaticMethod() {
+    this.#PRIVATE_STATIC_FIELD = 42
+    return this.#PRIVATE_STATIC_FIELD
+  }
+}
+
+class SubClass extends BaseClassWithPrivateStaticField { }
+
+let error = null
+
+try {
+  SubClass.basePublicStaticMethod()
+} catch(e) { error = e}
+
+console.assert(error instanceof TypeError)
+
+ +

Приватные поля экземпляров

+ +

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

+ +

Инкапсуляция обеспечивается языком. Обращение к # именам вне области видимости является синтаксической ошибкой.

+ +
class ClassWithPrivateField {
+  #privateField
+
+  constructor() {
+    this.#privateField = 42
+    this.#randomField = 666 // Syntax error
+  }
+}
+
+const instance = new ClassWithPrivateField()
+instance.#privateField === 42 // Syntax error
+
+ +

Приватные методы

+ +

Приватные статические методы

+ +

Приватные статические методы

+ +

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

+ +

Приватные статические методы могут быть генераторами, асинхронными функциями и асинхронными функциями-генераторами.

+ +
class ClassWithPrivateStaticMethod {
+    static #privateStaticMethod() {
+        return 42
+    }
+
+    static publicStaticMethod1() {
+        return ClassWithPrivateStaticMethod.#privateStaticMethod();
+    }
+
+    static publicStaticMethod2() {
+        return this.#privateStaticMethod();
+    }
+}
+
+console.assert(ClassWithPrivateStaticMethod.publicStaticMethod1() === 42);
+console.assert(ClassWithPrivateStaticMethod.publicStaticMethod2() === 42);
+
+ +

Это может привести к неожиданному поведению при его использовании this. В следующем примере this относится к классу Derived (а не к классу Base), когда мы пытаемся вызвать Derived.publicStaticMethod2(), и, таким образом, имеет такое же "ограничение по происхождению", как упоминалось выше:

+ +
class Base {
+    static #privateStaticMethod() {
+        return 42;
+    }
+    static publicStaticMethod1() {
+        return Base.#privateStaticMethod();
+    }
+    static publicStaticMethod2() {
+        return this.#privateStaticMethod();
+    }
+}
+
+class Derived extends Base {}
+
+console.log(Derived.publicStaticMethod1()); // 42
+console.log(Derived.publicStaticMethod2()); // TypeError
+
+ +

Приватные методы экземпляров(instance)

+ +

Приватные методы экземпляров это методы, доступные у экземпляров класса, доступ к которым запрещен также, как у приватных полей класса.

+ +
class ClassWithPrivateMethod {
+  #privateMethod() {
+    return 'hello world'
+  }
+
+  getPrivateMessage() {
+      return this.#privateMethod()
+  }
+}
+
+const instance = new ClassWithPrivateMethod()
+console.log(instance.getPrivateMessage())
+// expected output: "hello worl​d"
+ +

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

+ +
class ClassWithPrivateAccessor {
+  #message
+
+  get #decoratedMessage() {
+    return `✨${this.#message}✨`
+  }
+  set #decoratedMessage(msg) {
+    this.#message = msg
+  }
+
+  constructor() {
+    this.#decoratedMessage = 'hello world'
+    console.log(this.#decoratedMessage)
+  }
+}
+
+new ClassWithPrivateAccessor();
+// expected output: "✨hello worl​d✨"
+
+ +

Спецификации

+ + + + + + + + + + + + +
Specification
{{SpecName('Public and private instance fields', '#prod-FieldDefinition', 'FieldDefinition')}}
+ +

Совместимость с браузерами

+ + + +

{{Compat("javascript.classes.private_class_fields")}}

+ +

См. также

+ + -- cgit v1.2.3-54-g00ecf