---
title: Классы
slug: Web/JavaScript/Reference/Classes
tags:
  - ECMAScript 2015
  - ECMAScript6
  - JavaScript
  - Reference
  - Классы
  - Наследование
translation_of: Web/JavaScript/Reference/Classes
---
<div>{{JsSidebar("Классы")}}</div>

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

<h2 id="Определение_классов">Определение классов</h2>

<p>На самом деле классы — это "специальные <a href="/ru/docs/Web/JavaScript/Reference/Functions">функции</a>", поэтому точно также, как вы определяете функции (<a href="/ru-RU/docs/Web/JavaScript/Reference/Operators/function">function expressions</a> и <a href="/ru-RU/docs/Web/JavaScript/Reference/Statements/function">function declarations</a>), вы можете определять и классы с помощью: <a href="/en-US/docs/Web/JavaScript/Reference/Statements/class">class declarations</a> и <a href="/en-US/docs/Web/JavaScript/Reference/Operators/class">class expressions</a>.</p>

<h3 id="Объявление_класса">Объявление класса</h3>

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

<pre class="brush: js notranslate"><code>class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}</code></pre>

<h4 id="Подъём_hoisting">Подъём (hoisting)</h4>

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

<pre class="brush: js notranslate"><code>var p = new Rectangle(); // ReferenceError

class Rectangle {}</code></pre>

<h3 id="Выражение_класса">Выражение класса</h3>

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

<pre class="brush: js notranslate"><code>// безымянный
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" </code>
</pre>

<div class="note">
<p><strong>Обратите внимание: выражения </strong>класса подвержены тем же проблемам с подъёмом (hoisting), что и <strong>объявления</strong> класса.</p>
</div>

<h2 id="Тело_класса_и_задание_методов">Тело класса и задание методов</h2>

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

<h3 id="Строгий_режим">Строгий режим</h3>

<p>Тела <em>объявлений классов</em> и <em>выражений классов</em> выполняются в строгом режиме (<a href="/en-US/docs/Web/JavaScript/Reference/Strict_mode">strict mode</a>).</p>

<h3 id="Constructor">Constructor</h3>

<p>Метод <code><a href="/ru/docs/Web/JavaScript/Reference/Classes/constructor">constructor</a></code> — специальный метод, необходимый для создания и инициализации объектов, созданных, с помощью класса. В классе может быть только один метод с именем <code>constructor</code>. Исключение типа {{jsxref("SyntaxError")}} будет выброшено, если класс содержит более одного вхождения метода <code>constructor</code>.</p>

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

<h3 id="Методы_прототипа">Методы прототипа</h3>

<p>См. также <a href="/ru/docs/Web/JavaScript/Reference/Functions/%D0%9E%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B8%D0%BD%D0%B8%D0%B5_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%B2">определение методов</a>.</p>

<pre class="brush: js notranslate"><code>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</code></pre>

<h3 id="Статические_методы_и_свойства">Статические методы  и свойства</h3>

<p>Ключевое слово <code><a href="/en-US/docs/Web/JavaScript/Reference/Classes/static">static</a></code>, определяет статический метод или свойства для класса. Статические методы и свойства вызываются без <a href="/ru/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript#The_Object_.28Class_Instance.29">инстанцирования</a> их класса, и <strong>не могут</strong> быть вызваны у экземпляров (<em>instance</em>) класса. Статические методы, часто используются для создания служебных функций для приложения, в то время как статические свойства полезны для кеширования в рамках класса, фиксированной конфигурации или любых других целей, не связанных с реплецированием данных между экземплярами.</p>

<pre class="brush: js notranslate"><code>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</code></pre>

<h3 id="Привязка_this_в_прототипных_и_статических_методах">Привязка <code>this</code> в прототипных и статических методах</h3>

<p>Когда статический или прототипный метод вызывается без привязки к <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">this</span></font> объекта (или когда <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">this</span></font> является типом boolean, string, number, undefined, null), тогда <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">this</span></font> будет иметь значение <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">undefined</span></font> внутри вызываемой функции. Автоупаковка не будет произведена. Поведение будет таким же как если бы мы писали код в нестрогом режиме.</p>

<pre class="brush: js notranslate"><code>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</code></pre>

<p>Если мы напишем этот же код используя классы основанные на функциях, тогда произойдет автоупаковка основанная на значении <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">this</span></font>, в течение которого функция была вызвана. В строгом режиме автоупаковка не произойдет - значение <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">this</span></font> останется прежним.</p>

<pre class="brush: js notranslate"><code>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(); // глобальный объект (нестрогий режим)</code>
</pre>

<h3 id="Свойства_экземпляра">Свойства экземпляра</h3>

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

<pre class="notranslate">class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}</pre>

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

<pre class="notranslate">Rectangle.staticWidth = 20;
Rectangle.prototype.prototypeWidth = 25;
</pre>

<h3 id="Определение_полей">Определение полей</h3>

<div class="blockIndicator warning">
<p>Публичные и приватные поля - это <a href="https://github.com/tc39/proposal-class-fields">экспериментальная особенность (stage 3)</a>, предложенная комитетом <a href="https://tc39.es/">TC39</a> по стандартам языка Javascript. Поддержка браузерами ограничена, но это нововведение может быть использовано на моменте сборки, используя к примеру <a href="https://babeljs.io/">Babel</a>.</p>
</div>

<h4 id="Публичные_поля">Публичные поля</h4>

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

<pre class="notranslate">class Rectangle {
  height = 0;
  width;
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}</pre>

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

<p>Более подробно об этом написано в <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Classes/Class_fields">публичные поля класса</a>.</p>

<h4 id="Приватные_поля">Приватные поля</h4>

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

<pre class="notranslate">class Rectangle {
  #height = 0;
  #width;
  constructor(height, width) {
    this.#height = height;
    this.#width = width;
  }
}</pre>

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

<div class="blockIndicator note">
<p>Приватные поля могут быть объявлены только заранее в объявлении поля.</p>
</div>

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

<p>Более подробно об этом написано в <a href="/ru/docs/Web/JavaScript/Reference/Classes/%D0%9F%D1%80%D0%B8%D0%B2%D0%B0%D1%82%D0%BD%D1%8B%D0%B5_%D0%BF%D0%BE%D0%BB%D1%8F_%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%B0">Приватные поля класса</a>.</p>

<h2 id="Наследование_классов_с_помощью_extends">Наследование классов с помощью <code>extends</code></h2>

<p>Ключевое слово <code><a href="/en-US/docs/Web/JavaScript/Reference/Classes/extends">extends</a></code> используется в <em>объявлениях классов</em> и <em>выражениях классов</em> для создания класса, дочернего относительно другого класса.</p>

<pre class="brush: js notranslate"><code class="language-js">class Animal {
  constructor(name) {
    this.name = name;
  }</code>

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

class Dog extends Animal {
  constructor(name) {
    super(name); // вызывает конструктор super класса и передает параметр name
  }

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

<code class="language-js">let d = new Dog('Митци');
d.speak(); // Митци лает</code></pre>

<p>Если в подклассе присутствует конструктор, он должен сначала вызвать <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">super</span></font>, прежде чем использовать <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">this</span></font>.</p>

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

<pre class="brush: js line-numbers  language-js notranslate"><code class="language-js">function Animal (name) {
  this.name = name;
}
Animal.prototype.speak = function () {
  console.log(</code>`${this.name} издает звук.`<code class="language-js">);
}

class Dog extends Animal {
  speak() {
    console.log(</code>`${this.name} лает.`<code class="language-js">);
  }
}

let d = new Dog('Митци');
d.speak(); // Митци лает

// </code>Для аналогичных методов дочерний метод имеет приоритет над родительским.</pre>

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

<pre class="brush: js line-numbers  language-js notranslate"><code class="language-js">var Animal = {
  speak() {
    console.log(</code>`${this.name} издает звук.`<code class="language-js">);
  }
};

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

// Если вы этого не сделаете, вы получите ошибку TypeError при вызове speak.
Object.setPrototypeOf(Dog.prototype, Animal);

let d = new Dog('Митци');
d.speak(); // Митци издает звук.</code></pre>

<h2 id="Species">Species</h2>

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

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

<pre class="brush: js line-numbers  language-js notranslate"><code class="language-js">class MyArray extends Array {
  // Изменить species на родительский конструктор Array
  static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x =&gt; x * x);

console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array);   // true</code></pre>

<h2 id="Обращение_к_родительскому_классу_с_помощью_super">Обращение к родительскому классу с помощью <code>super</code></h2>

<p>Ключевое слово <code><a href="/ru/docs/Web/JavaScript/Reference/Operators/super">super</a></code> используется для вызова функций на родителе объекта.</p>

<pre class="brush: js  language-js notranslate"><code class="language-js">class Cat {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(</code>`${this.name} издает звук.`<code class="language-js">);
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(</code>`${this.name}<code class="language-js"> рычит.`);
  }
}

let l = new Lion('Фаззи');
l.speak();
// Фаззи издает звук.
// Фаззи рычит.</code>
</pre>

<h2 id="Mix-ins">Mix-ins</h2>

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

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

<pre class="brush: js line-numbers  language-js notranslate"><code class="language-js">var calculatorMixin = Base =&gt; class extends Base {
  calc() { }
};

var randomizerMixin = Base =&gt; class extends Base {
  randomize() { }
};</code></pre>

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

<pre class="brush: js line-numbers  language-js notranslate"><code class="language-js">class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }</code></pre>

<h2 id="Спецификации">Спецификации</h2>

<table class="standard-table">
 <tbody>
  <tr>
   <th scope="col">Спецификация</th>
   <th scope="col">Статус</th>
   <th scope="col">Комментарий</th>
  </tr>
  <tr>
   <td>{{SpecName('ES6', '#sec-class-definitions', 'Class definitions')}}</td>
   <td>{{Spec2('ES6')}}</td>
   <td>Изначальное определение.</td>
  </tr>
  <tr>
   <td>{{SpecName('ESDraft', '#sec-class-definitions', 'Class definitions')}}</td>
   <td>{{Spec2('ESDraft')}}</td>
   <td></td>
  </tr>
 </tbody>
</table>

<h2 id="Совместимость_с_браузерами">Совместимость с браузерами</h2>

<p>{{Compat("javascript.classes")}}</p>

<h2 id="Повторное_определение_класа">Повторное определение класа</h2>

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

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

<h2 id="Смотрите_также">Смотрите также</h2>

<ul>
 <li><a href="/en-US/docs/Web/JavaScript/Reference/Functions">Функции</a></li>
 <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Statements/class">Определение классов</a></li>
 <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/class">Выражение классов</a></li>
 <li><a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Classes/Class_fields">Публичные поля класса</a></li>
 <li><a href="/ru/docs/Web/JavaScript/Reference/Classes/%D0%9F%D1%80%D0%B8%D0%B2%D0%B0%D1%82%D0%BD%D1%8B%D0%B5_%D0%BF%D0%BE%D0%BB%D1%8F_%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%B0">Приватные поля класса</a></li>
 <li><a href="/ru/docs/Web/JavaScript/Reference/Operators/super">super</a></li>
 <li><a href="https://hacks.mozilla.org/2015/07/es6-in-depth-classes/">Статья в блоге: "ES6 In Depth: Classes"</a></li>
 <li><a href="https://github.com/tc39/proposal-class-fields">Fields and public/private class properties proposal (stage 3)</a></li>
</ul>