JS изначально проектировался как очень простой и легкий язык, в нем заложено много концепций, которые делает язык не похожим на другие.
Например, иерархия объектов в JS строиться на основе прототипов и прототипного наследования. Что бы понять как это устроено, нужно уделить довольно много времени, и так как cейчас прототипы при разработке используются очень редко, мы их рассматривать не будем, но вы можете почитать как они работают в онлайн учебнике: learn.javascript
Раньше разработчики с помощью них создавали конструции, подобные классам в других ОО-языках. На слайде пример создая класса с использованием старого синтаксиса, используя прототип и функцию.
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.outputPoint = function() {
console.log(this.x, this.y);
}
const point = new Point(10, 25);
point.outputPoint(); // -> 10, 25
Но в стандарте ES6 представили новый синтаксис для классов. Класс ES6 не представляет собой новую объектно-ориентированную модель наследования, это просто синтаксический сахар для существующего в JavaScript прототипного наследования.
Также появился метод constructor, который запускается при создании экземпляра класса
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
outputPoint() {console.log(this.x, this.y);}
}
const point = new Point(10, 25);
point.outputPoint(); // -> 10, 25
В ES6 (ES7) теперь можно объявлять статические свойства класса. К статическим свойствам класса можно обращаться без создания экземпляра. Зачастую в статические свойства записывают константы.
class Point {
static name = 'SomePoint';
outputName() {
console.log(Point.name);
}
}
let point = new Point();
point.outputName(); // -> SomePoint
console.log(Point.name); // -> SomePoint
В классах, как и в обычных объектах, можно объявлять геттеры и сеттеры через get/set. Но, стоит помнить, class не позволяет задавать свойства-значения
class User {
constructor({firstName, lastName}) {
this.firstName = firstName;
this.lastName = lastName;
}
// геттер
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
// сеттер
set fullName(newValue) {
[this.firstName, this.lastName] = newValue.split(' ');
}
}
let user = new User('Mike', 'Brown');
console.log(user.fullName); // Mike Brown
user.fullName = 'Piter Parker';
console.log(user.fullName); // Piter Parker
Для наследования используется директива extends
. В примере ниже мы используем директиву super(...)
, таким образом мы вызываем конструктор родителя и можем туда передать аргументы. Если при наследование у дочернего класса нет метода constructor, то автоматически присвоится конструктор родителя.
class User {
constructor(name) {
this.name = name;
}
whoAmI() {
console.log( My name is ${this.name} );
}
}
class Admin extends User {
constructor(name) {
super(name);
}
}
// -> My name is John
new Admin('John').whoAmI();
В ES6 очень удобно стало вызывать родительские методы, раньше необходимо было использовать конструкцию Parent.prototype.method
(с использованием прототипа), теперь все стало проще, можно использовать директиву super
.
class User {
constructor(name) {
this.name = name;
}
whoAmI() {
console.log( My name is ${this.name} );
}
}
class Admin extends User {
whoAmI() {
console.log('I\'m admin');
super.whoAmI();
}
}
// -> I'm admin\n My name is John
new Admin('John').whoAmI();
Оператор instanceof
позволяет проверить, какому классу принадлежит объект, с учетом прототипного наследования. Вызов obj instanceof Constructor
возвращает true
, если объект принадлежит классу Constructor
или классу, наследующему от него.
class Cat() {}
const persik = new Cat(); // создаём объект
// проверяем - этот объект создан Cat?
console.log(persik instanceof Cat); // true, верно
Если аргументов много, их можно передавать в одном объекте, что дает огромный плюс, не нужно помнить о порядке передачи аргументов и в коде сразу можно понять, какие поля передаются в конструктор (метод) по ключам. Этот подход используется не только для конструкторов, но и для всех функций.
class User {
constructor(firstName, lastName, language = 'en') {
this.firstName = firstName;
this.lastName = lastName;
this.language = language;
}
}
const alex = new User('Alex', 'Poter', 'ru');
const tom = new User('Tom', 'Cruise');
class User {
- constructor(firstName, lastName, language = 'en') {
+ constructor({firstName, lastName, language = 'en'}) {
}
}
- const alex = new User('Alex', 'Poter', 'ru');
+ const alex = new User({firstName: 'Alex', lastName: 'Poter', language: 'ru'});