Classes

Переклад не закінчено. Будь ласка, допоможіть перекласти цю статтю з англійської.

JavaScript класи були додані в стандарті ECMAScript 6 і є синтаксичним цукром для вже існуючого прототипного унаслідовання в JavaScript. Синтаксис у вигляді класів не означає, що в JavaScript з’явилася нова об’єктно орієнтована-модель. JavaScript класи лише надають більш простий і чистий синтаксис для створення об’єктів і роботи з наслідуванням.

Що таке класи?

Класи - це по факту "особливі функції". Так само як функція, клас складається з оголошення та реалізації.

Щоб оголосити новий клас, ти можеш використати ключове слово class, після якого буде слідувати назва самого класу (в прикладі нижче "Polygon").

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

Hoisting

Важлива різниця між function declarations і class declarations у тому, що  function declarations є hoisted, тобто в коді можна зверху використати функцію і описати її нижче по коду.  а class declarations не є таким. Вам потрібно спочатку описати ваш клас і тільки потім (нижче по коду) використовувати його. У іншому випадку код, наведенний нижче, поверне помилку. ReferenceError:

var p = new Polygon(); // ReferenceError

class Polygon {}

Class expressions

Class expression - це інших шлях описати клас. Class expressions може бути як іменованим, так і не іменованим. The name given to a named class expression is local to the class's body.

// unnamed
var Polygon = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

// named
var Polygon = class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

Class body and method definitions

Тіло класу знаходиться у фігурних скобках {}. Там ви описуєте складові класу, такі як методи і конструктори.

Strict mode

Код в тілі класу і class expressions завжди виконується у strict mode.

Constructor

Конструктор (constructor)- це спеціальний метод створення та ініціалізації нових об'єктів на основі класу. У класі може бути лише один метод Constructor. Якщо в класі опублікувати декілька методів Constructor, то виникне помилка SyntaxError.

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

Prototype methods (методи прототипу, у т.ч. геттери)

Дивись також method definitions

Методи описуються у вигляді заголовку метода (назва, параметри) і тіло метода. Приклад в коді на рядку 11.  Загалом, опис методу дуже схожий на звичайну функцію.

Інколи є потреба, щоб екземпляр класу повертав якесь значення, яке залежить від інших властивостей об'єкта. Також, ми можемо хотіти "сховати"  алгоритм отримання цього значення. Для цього можна використати ключове слово get (геттер). У прикладі нижче описують геттер area() (рядок 7). Екземпляр класу звертається до геттеру як до звичайної властивості об'єкта  square.area (рядок 18). 

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  
  get area() {
    return this.calcArea();
  }

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

const square = new Polygon(10, 10);

console.log(square.area);

Static methods

Ключове слово static вказує на статичні методи класа. Використання статичних методів не потребує створення об'єктів екземпляра класу і не може викликатися у екземпляра класу (called without instantiating their class). Статичні методи часто використовують у якості загальних службових функцій. У прикладі нижче загальний статичний метода розраховує відстань між екземплярам класу (об'єктами).

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

    static distance(a, b) {
        const dx = a.x - b.x;
        const dy = a.y - b.y;

        return Math.sqrt(dx*dx + dy*dy);
    }
}

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

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

Boxing with prototype and static methods

В Java Script функції і методи - це також об'єкти, які мають посилання на себе. Посилання на метод можна передавати в іншу змінну або у властивість іншого об'єкта. Так, у прикладі нижче клас Animal має метод speak(). У рядку 12 метод екземпляру цього класу передається у змінну speak. У рядку 13 до цієї змінної звертаються, як до звичайної функції.

Зверніть увагу. Що в такому випадку змінюється контекст виконання метода. Це означає, що ключове слово "this" може більше не посилатися на потрібний об'єкт.

Якщо метод викликається без прив'язки до об'єкту (рядки 13 і 16), або якщо у якості об'єкта є змінна примітивних типів boolean, string, number, undefined or null, то в середині методу "this" буде мати значення undefined.  У прикладі нижче методи speak() та eat() повертають undefined.

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

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

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

Щоб виправити це, потрібно прив'язати метод до об'єкту за допомогою bind().

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

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

Також, можна викликати функцію за допомогою методів call() або apply()

let obj = new Animal();
let speak = obj.speak;
speak.call(obj); // або speak.apply(obj)

let eat = Animal.eat.bind(obj);
eat.call(obj); // або eat.apply(obj)

Зауважте, що "this" отримує значення undefined, бо всередині класу використовується строгий режим ('use strict'). Якщо переписати наш приклад на традиційний опис класів у вигляді функцій (без 'use strict'), то замісь undefined в "this" буде посилання на глобальний scope.  У такому випадку, також, варто пам'ятати про методи bind, apply, call.
 

function Animal() { }

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

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

let obj = new Animal();
let speak = obj.speak;
speak(); // global object

let eat = Animal.eat;
eat(); // global object


Sub classing with extends (наслідування класів за допомогою extends)

Ключове слово extends використовують в class declarations або class expressions, щоб створити клас, як "дитину" від іншого класу. Також, це називають наслідуванням класів. Клас, який наслідують називається батьківським або суперклас. "Дитячий" клас, також, називають підклас або підклас. У прикладі нижче Animal є батьківським класом. А клас Dog  наслідує клас Animal за допомогю слова extends.

class Animal { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Dog extends Animal {
  speak() {
    console.log(this.name + ' barks.');
  }
}

Також, можна додавати методи класу, як для традиційного класу-функції. У прикладі нижче таким чином у клас додається метод speak() через ключове слово prototype.

function Animal (name) {
  this.name = name;  
}
Animal.prototype.speak = function () {
  console.log(this.name + ' makes a noise.');
}

class Dog extends Animal {
  speak() {
    super.speak();
    console.log(this.name + ' barks.');
  }
}

var d = new Dog('Mitzie');
d.speak();

Species

You might want to return Array objects in your derived array class MyArray. The species pattern lets you override default constructors.

For example, when using methods such as map() that returns the default constructor, you want these methods to return a parent Array object, instead of the MyArray object. The Symbol.species symbol lets you do this:

class MyArray extends Array {
  // Overwrite species to the parent Array constructor
  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 class calls with super (виклик методів батьківського класу за допомогою super)

Ключове слово super використовують для виклику функцій батьківського класу. У прикладі нижче клас Lion наслідує клас Cat. Тобто Cat  є батьківським класом. Наслідування відбувається за допомогую слова extends.

class Cat { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(this.name + ' roars.');
  }
}

Mix-ins

Абстрактні підкласи або mix-ins це шаблони для створення класів. В ECMAScript підклас може наслідувати лише один суперклас. Тому наслідування одразу багатьох суперкласів неможливе. 

Наслідування багатьох класів можна зробити, як у прикладі нижче. Опубліковані дві функції CalculatorMixin() і RandomizerMixin(). У якості параметру в ці функції передають суперклас Base. Обидві функції повертають підкласи, які наслідують клас Base:

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

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

Далі класи, які використовують ці mix-ins, можуть створюватись як у прикладі нижче. Створюється клас  Foo. Клас Foo послідовно передається у функції-міксини RandomizerMixin та  CalculatorMixin. Таким чином отримують клас, який має методи calc() і randomize(). На основі результату міксин створюють новий клас Bar:

class Foo { }
class Bar extends CalculatorMixin(RandomizerMixin(Foo)) { }

Specifications

Specification Status Comment
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Class definitions' in that specification.
Standard Initial definition.
ECMAScript Latest Draft (ECMA-262)
The definition of 'Class definitions' in that specification.
Draft  

Browser compatibility

Feature Chrome Firefox (Gecko) Edge Internet Explorer Opera Safari
Basic support 42.0[1]
49.0
45 13 No support No support 9.0
Feature Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile Chrome for Android
Basic support No support 45 ? ? 9 42.0[1]
49.0

[1] Requires strict mode. Non-strict mode support is behind the flag "Enable Experimental JavaScript", disabled by default.

See also

Мітки документа й учасники

 Зробили внесок у цю сторінку: tjs, bsurai, georgelviv, rv170
 Востаннє оновлена: tjs,