--- title: Classes slug: Web/JavaScript/Reference/Classes tags: - Classes - Constructors - ECMAScript 2015 - Inheritance - Intermediate - JavaScript - TopicStub translation_of: Web/JavaScript/Reference/Classes ---
Klasy w Javascript zostały wprowadzone w ECMAScript 2015 jako lukier składniowy (ang. syntactic sugar) dla istniejącego, opartego na prototypach modelu dziedziczenia. Składnia klas nie wprowadza nowego zorientowanego obiektowo modelu dziedziczenia. Klasy wprowadzają znacznie prostszą i bardziej czytelną składnię do tworzenia obiektów i dziedziczenia.
Klasy są w zasadzie "szczególnymi funkcjami". Podobnie jak w funkcji można definiować wyrażenie function
i deklaracje funkcji, tak składnia klasy posiada dwa komponenty: wyrażenie class
i deklaracje klasy.
Jednym ze sposobów definiowania klas jest deklaracja klasy. Aby zadeklarować klasę, należy użyć słowa kluczowego class
wraz z nazwą klasy (w tym przypadku "Prostokat").
class Prostokat { constructor(wysokosc, szerokosc) { this.wysokosc = wysokosc; this.szerokosc = szerokosc; } }
Ważną różnicą pomiędzy deklaracją funkcji a deklaracją klasy jest to, że deklaracje funkcji są przenoszone na początek ({{Glossary("Hoisting")}}) a klas nie. Najpierw musisz zadeklarować swoją klasę, by mieć do niej dostęp, w przeciwnym razie kod, jak ten poniżej, wygeneruje błąd {{jsxref("ReferenceError")}}:
var p = new Prostokat(); // ReferenceError class Prostokat {}
class
Wyrażenie class
jest kolejnym sposobem definiowania klasy. Wyrażenia class
mogą być nazwane lub nienazwane. Nazwa przypisana nazwanemu wyrażeniu class
jest lokalna dla ciała klasy. (można ją odczytać z właściwości {{jsxref("Function.name", "name")}} klasy)
// nienazwane var Prostokat = class { constructor(wysokosc, szerokosc) { this.wysokosc = wysokosc; this.szerokosc = szerokosc; } }; console.log(Prostokat.name); // Prostokat // nazwane var Prostokat = class Prostokat2 { constructor(wysokosc, szerokosc) { this.wysokosc = wysokosc; this.szerokosc = szerokosc; } }; console.log(Prostokat.name); // Prostokat2
Uwaga: Wyrażenia class
dotykają te same kwestie związane z przenoszeniem na początek (ang. hoisting) co wspomnianych deklaracji klas.
Ciało klasy jest umieszczane w nawiasach klamrowych {}
. To tam definiuje się metody, czy konstruktory.
Ciało klasy jest wykonywane w trybie ścisłym (ang. strict mode). W celu poprawienia wydajności, kod wykorzystywany tutaj podlega ścisłej składni; nie pozwala to na ukrycie niektórych wyjątków, a pewne słowa kluczowe są rezerwowane dla przyszłych wersji ECMAScript.
Constructor
jest szczególną metodą, która służy tworzeniu i inicjalizowaniu obiektu zdefiniowanego słowem kluczowym class
. Dozwolony jest tylko jeden konstruktor w danej klasie. Jeśli klasa posiada więcej niż jedno wystąpienie metody constructor
, wygenerowany zostanie błąd {{jsxref("SyntaxError")}}.
Aby wywołać konstruktor klasy bazowej, należy użyć słowa kluczowego super
.
Zobacz też definiowanie metod.
class Prostokat { constructor(wysokosc, szerokosc) { this.wysokosc = wysokosc; this.szerokosc = szerokosc; } // Getter get pole() { return this.liczPole(); } // Method liczPole() { return this.wysokosc * this.szerokosc; } } const kwadrat = new Prostokat(10, 10); console.log(kwadrat.pole); // 100
Słowo kluczowe static
definiuje metodę kub właściwość statyczną w klasie. Statyczne metody i właściwości są wywoływane bez inicjalizowania ich klas i nie mogą być wywołane przez instancję klasy.
class Punkt { constructor(x, y) { this.x = x; this.y = y; } static nazwa = "Punkt"; static odleglosc(a, b) { const dx = a.x - b.x; const dy = a.y - b.y; return Math.sqrt(dx*dx + dy*dy); } } const p1 = new Punkt(5, 5); const p2 = new Punkt(10, 10); p1.nazwa; // undefined p1.odleglosc; // undefined p2.nazwa; // undefined p2.odleglosc; // undefined console.log(Punkt.nazwa); // "Punkt" console.log(Punkt.odleglosc(p1, p2)); // 7.0710678118654755
this
z metodami niestatycznymi i statycznymiKiedy metoda typu static
lub prototype
jest wywoływana bez this
(na przykład poprzez przypisanie metody do zmiennej), wtedy this
będzie undefined
w środku metody. Takie zachowanie będzie takie same, nawet jeżeli dyrektywa "use strict"
nie będzie obecna, ponieważ kod w obrębie metody danej klasy zawsze będzie wykonywał się jako strict mode
.
class Animal { speak() { return this; } static eat() { return this; } } let obj = new Animal(); obj.speak(); // obiekt Animal let speak = obj.speak; speak(); // undefined Animal.eat(); // klasa Animal let eat = Animal.eat; eat(); // undefined
Jeśli przepiszemy powyższy przykład z użyciem tradycyjnych funkcji bez dyrektywy "use strict"
, to this
wywołane w metodzie będzie automatycznie przypisane do pierwotnej wartości this
, którą domyślnie jest global object.
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
Właściwości instancji muszą być zdefiniowane wewnątrz metody klasy:
class Rectangle { constructor(height, width) { this.height = height; this.width = width; } }
Statyczne właściwości i właściwości prototypu muszą być zdefiniowane poza ciałem klasy:
Rectangle.staticWidth = 20; Rectangle.prototype.prototypeWidth = 25;
Publiczna i prywatne deklaracje pól są funkcjonalnościami eksperymentalnymi zaproponowanymi na TC39. Wsparcie przeglądarek jest ograniczone, ale ta funkcjonalność może być używana przy użyciu systemów takich jak Babel.
Przy użyciu deklaracji pól, powyższy przykład może być przepisany na:
class Rectangle { height = 0; width; constructor(height, width) { this.height = height; this.width = width; } }
Dzięki deklarowaniu pól na początku klasy, definicje klas stają się bardziej samodokumentujące, a pola są zawsze obecne.
Jak widać w powyższym przykładzie, pola mogą być zadeklarowane z lub bez domyślnej wartości.
Zobacz public class fields po więcej informacji.
Używając deklaracji pól prywatnych, definicja może być zapisana w taki sposób:
class Rectangle { #height = 0; #width; constructor(height, width) { this.#height = height; this.#width = width; } }
Próba odniesienia się do prywatnego pola poza ciałem klasy wygeneruje błąd. Prywatne pola mogą być tylko odczytywane i modyfikowane wewnątrz ciała klasy. Poprzez definicję właściwości niewidocznych poza ciałem klasy, można zapewnić, że użytkownicy klasy nie będą polegali na jej wewnętrznych właściwościach.
Pola prywatne mogą być tylko zadeklarowane na początku ciała klasy
Prywatnych pól nie da się utworzyć później, poprzez przypisywanie, tak jak normalnych właściwości.
Po więcej informacji zobacz private class fields.
extends
Słowo kluczowe extends
jest używane w deklaracjach klas lub wyrażeniach klas do tworzenia klasy jako elementu potomnego innej klasy.
class Animal { constructor(name) { this.name = name; } speak() { console.log(this.name + ' makes a noise.'); } } class Dog extends Animal { constructor(name) { super(name); // wywyłanie konstruktora klasy nadrzędnej poprzez użycie super() } speak() { console.log(this.name + ' barks.'); } } let d = new Dog('Mitzie'); d.speak(); // Mitzie barks.
Jeśli w podklasie znajduje się konstruktor, musi najpierw wywołać super() przed użyciem "this".
Można również rozszerzyć tradycyjne klasy oparte na funkcjach:
function Animal (name) { this.name = name; } Animal.prototype.speak = function () { console.log(this.name + ' makes a noise.'); } class Dog extends Animal { speak() { console.log(this.name + ' barks.'); } } let d = new Dog('Mitzie'); d.speak(); // Mitzie barks
Zwróć uwagę, że klasy nie mogą rozszerzać zwykłych (niezdatnych do konstrukcji) obiektów. Jeśli chcesz dziedziczyć po zwykłym obiekcie, możesz, zamiast tego użyć {{jsxref ("Object.setPrototypeOf()")}}:
var Animal = { speak() { console.log(this.name + ' makes a noise.'); } }; class Dog { constructor(name) { this.name = name; } } Object.setPrototypeOf(Dog.prototype, Animal);// If you do not do this you will get a TypeError when you invoke speak let d = new Dog('Mitzie'); d.speak(); //Mitzie makes a noise.
Jeśli chcesz zwrócić obiekt {{jsxref("Array")}} w twojej klasie MyArray
, która dziedziczy po Array
, to możesz użyć wzorca "species", który pozwala na nadpisywanie domyślnych konstruktorów.
Na przykład, wywołanie metody {{jsxref("Array.map", "map()")}} zwraca domyślny konstruktor MyArray
. Użycie {{jsxref("Symbol.species")}} pozwala na nadpisanie tego zachowania tak, by zwracany był obiekt typu Array
, a nie MyArray
:
class MyArray extends Array { // Nadpisanie domyślnego kontruktora 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
Słowo kluczowe super jest wykorzystywane do udostępniania i korzystania z funkcji klasy, po której nasz obiekt dziedziczy.
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.`); } } let l = new Lion('Fuzzy'); l.speak(); // Fuzzy makes a noise. // Fuzzy roars.
Abstrakcyjne podklasy lub mix-ins są szablonami dla klas. Klasa może mieć tylko jedną klasę nadrzędną, więc dziedziczenie z wielu klas jest niemożliwe. Cała funkcjonalność musi być dostarczona przez jedną klasę nadrzędną.
Funkcja przyjmująca klasę nadrzędną jako argument i zwracająca podklasę rozszerzającą klasę nadrzędną może być użyta do implementacji mix-in'ów:
var calculatorMixin = Base => class extends Base { calc() { } }; var randomizerMixin = Base => class extends Base { randomize() { } };
Klasa używająca tych mix-in'ów może być zapisana w taki sposób:
class Foo { } class Bar extends calculatorMixin(randomizerMixin(Foo)) { }
Specyfikacja | Status | Komentarz |
---|---|---|
{{SpecName('ES2015', '#sec-class-definitions', 'Class definitions')}} | {{Spec2('ES2015')}} | Initial definition. |
{{SpecName('ESDraft', '#sec-class-definitions', 'Class definitions')}} | {{Spec2('ESDraft')}} |
{{Compat("javascript.classes")}}
class
declarationclass
expression