Время прочтения: 7 мин.
В материале использовался jQuery 3.4.1 и браузеры ЯндексБраузер 22.7.5.940 (64-bit), Microsoft Edge 105.0.1343.27 (Официальная сборка) (64-разрядная версия).
Ранее я показывала пример с формой регистрации, в котором рассматривались: методы и переменные класса, сеттеры и геттеры, вызов объектов класса. В этой части я покажу на примере: как использовать наследование, инкапсуляцию и полиморфизм в JS. Если вы еще не знакомы с первой частью, то рекомендую сначала прочитать ее.
Прежде чем начать, необходимо создать файловую структуру и вспомогательные скрипты для дальнейшей демонстрации.
Изменю файловую структуру формы регистрации из предыдущей части следующим образом: Создам в корне проекта каталоги «static», «template». Перенесу в «static» ранее созданные папки css, js, lib; а в директорию «template» перенесу файл index.html.

В ООП программировании существуют основные принципы:
- Наследование
- Полиморфизм
- Инкапсуляция
Также существует принцип абстракции, но здесь он рассматриваться не будет.
Рассмотрим каждый принцип в JS и разберем на примерах по порядку.
Наследование – это когда один класс получает свойства и методы другого класса.
Создам файл workspace\form_es6\static\js\hello.js с классом hello и методами, которые будут возвращать приветствие в зависимости от времени суток:
hello.js
class hello {
constructor(v_curdatetime){
//текущее дата/время
this.curdatetime = v_curdatetime;
//массив приветствия
this.arr_goodday = new Array("Доброе утро", "Добрый день", "Добрый вечер", "Доброй ночи");
}
setHour(datetime){
const hh = datetime.getHours();
return hh;
}
salute(v_fio){
const hh = this.setHour(this.curdatetime);
let out = '';
let str_goodday = '';
if(hh >= 6 && hh < 12){
str_goodday = this.arr_goodday[0];
out = str_goodday +" "+ v_fio;
}
if(hh >= 12 && hh < 18){
str_goodday = this.arr_goodday[1];
out = str_goodday +" "+ v_fio;
}
if(hh >= 18 && hh < 0){
str_goodday = this.arr_goodday[2];
out = str_goodday +" "+ v_fio;
}
if(hh >= 0 && hh < 6){
str_goodday = this.arr_goodday[3];
out = str_goodday +" "+ v_fio;
}
console.log(out);
return out;
}
}
Добавлю в файл workspace\form_es6\template\index.html линк на новый файл hello.js:
<script type="text/javascript" src="/static/js/hello.js"></script>
<script type="text/javascript" src="/static/js/form1ES6.js"></script>
</head>
В файле workspace\form_es6\static\js\form1ES6.js и изменю строки, как представлено ниже:
form1ES6.js
class form1 extends hello{
constructor(curdatetime){
super(curdatetime);
…
}
}
Теперь класс form1 наследует свойства и методы класса hello. В данном случае класс form1 является дочерним классом, а класс hello – это суперкласс или родительский класс. Ключевое слово extends означает связь между двумя классами. Ключевое слово super означает, что в конструкторе дочернего класса form1 вызывается конструктор родительского класса hello. В конструктор родительского класса передаются значения переменных дочернего класса.
При вызове дочернего класса выполнятся не только методы класса form1, но и класса hello.
Можно вызвать метод salute родительского класса «hello» при загрузке страницы в событии «Submit»:
$(document).ready(function() {
let curdatetime = new Date();
const objform1 = new form1(curdatetime); //Передаем значение текущего времени
//можно вызвать метод salute родительского класса «hello»
//при загрузке страницы в событии «Submit»
$("#registrity").submit(function(event){
objform1.salute($("#fio").val());
});
})
или вызвать этот метод в дочернем классе:
form1ES6.js
class form1 extends hello{
…
init(){
…
this.form1.on('submit',(e)=>{
const fio = this.fio.val();
…
const goodday_str = this.salute(fio);
…
return false;
});
}
Заполнив форму в браузере и нажав кнопку «Отправить», можно увидеть результаты в панели консоли, которые возвращает метод salute(fio) класса hello:

Полиморфизм – дословно означает: «много форм». Это возможность объектов и методов с одинаковыми наименованиями принимать разные формы.
Полиморфизм бывает двух основных типов:
- Переопределение методов
- Перегрузка функций
Переопределение методов – это когда при наследовании классов функцию родительского класса можно переопределить в дочернем классе. Покажу на примере.
В файле workspace/form_es6/static/js/form1ES6.js добавлю в класс «form1» метод «salute»:
form1ES6.js
salute(v_fio){
const hh = this.setHour(this.curdatetime);
let out = '';
let str_goodday = '';
if(hh >= 6 && hh < 12){
str_goodday = 'Good morning';
out = str_goodday +" "+ v_fio;
}
if(hh >= 12 && hh < 18){
str_goodday = 'Good afternoon';
out = str_goodday +" "+v_fio
}
if(hh >= 18 && hh < 0){
str_goodday = 'Good evening';
out = str_goodday +" "+v_fio
}
if(hh >= 0 && hh < 6){
str_goodday = 'Good night';
out = str_goodday +" "+v_fio
}
console.log(out);
return out;
}
Затем вызову метод «salute» при загрузке страницы:
$(document).ready(function() {
let curdatetime = new Date();
const objform1 = new form1(curdatetime); //Передаем значение текущего времени
$("#registrity").submit(function(event){
objform1.salute($("#fio").val()); //вызов метода «salute» с параметром fio
});
})
Теперь в нашем проекте два метода «salute»: один метод в родительском классе hello, другой – в дочернем классе form1. Но при вызове метода «salute» в сабмите формы выведется результат дочернего метода:

Таким образом, можно не только переопределить метод, но и дополнить (расширить) его дополнительными свойствами. Например, если в методе «salute» в классе form1 добавить вызов метода родительского класса «hello» — super.salute(v_fio)
salute(v_fio){
const hh = this.setHour(this.curdatetime);
let out = '';
let str_goodday = '';
super.salute(v_fio);
if(hh >= 6 && hh < 12){
str_goodday = 'Good morning';
out = str_goodday +" "+ v_fio;
}
…
console.log(out);
return out;
}
, то получится результат двух методов разных классов:

Перезагрузка методов – это создание нескольких функций с одинаковыми наименованиями, но разным количеством параметров или разными типами.
Т.е., образно говоря:
Func1(val1='', val2=''){
return val1+' '+ val2;
}
Func1(val1=0){
return val1;
}
Если выполнить функцию: Func1(‘go’, ‘walk’), то вернется результат: ‘go walk’.
А если выполнить функцию Func1(10), то вернется результат: 10.
В большинстве языков с поддержкой ООП такой полиморфизм работает, но не в JS. Если создать две функции с одинаковыми наименованиями, но разными входными параметрами, то JS все равно переопределит функцию. Но в JS есть схожий механизм с перезагрузкой методов при использовании сеттеров и геттеров. В первой части статьи я приводила пример и показывала, как работают вызовы:
//описание в классе
get HelloFIO(){
console.log('getter: '+this.hfio);
return this.hfio;
}
set HelloFIO(helloWorld){
console.log('setter: '+helloWorld);
this.hfio = helloWorld;
}
При вызове данного метода будет возвращен разный результат в зависимости от входных параметров этих методов:
$(document).ready(function() {
const objform1 = new form1('Доброе утро');
objform1.HelloFIO; //Вызываем без параметра, сработает getter
objform1.HelloFIO= 'Добрый день'; //Вызываем с параметром = 'Добрый день', сработает setter
})

Инкапсуляция – защита связанных данных объекта от влияния извне.
До некоторых пор приватные методы и приватные переменные JS не поддерживало. И ранее, по общей договоренности разработчиков, нижнее подчеркивание перед наименованием методов и переменных означало приватность:
_pass;
this._pass;
_func1();
Для самого же JS это не имело значения в плане видимости. Но, на данный момент, самые последние браузеры стали действительно поддерживать настоящую приватность методов и переменных класса. Чтобы сделать метод или переменную класса приватным, необходимо перед наименованием поставить знак «#». Пример, я создала приватную переменную #pass:
class form1 extends hello{
#pass;
constructor(curdatetime, v_fio, v_pass){
super(curdatetime);
…
//приватная переменная
this.#pass = v_pass;
this.init();
}
Теперь, при обращении к ней
$(document).ready(function() {
const objform1 = new form1(curdatetime,);
$("#registrity").submit(function(event){
objform1.#pass; //обращение к приватной переменной
});
})
браузер, который поддерживает данный синтаксис, вернет ошибку:
Uncaught SyntaxError: Private field '#pass' must be declared in an enclosing class
Таким образом, нельзя напрямую обратиться к приватной переменной извне.
Но можно вернуть ее в методе класса:
get_pass () {
return this.#pass;
}
При вызове этого метода извне, переменная вернет значение:
$(document).ready(function() {
const objform1 = new form1(curdatetime, 'parol1111'); //Передаем значение текущего времени и строчку с паролем
$("#registrity").submit(function(event){
//objform1.#pass;
objform1.get_pass();
});
К сожалению, данный синтаксис не является кросс-браузерным, поэтому на данном этапе не рекомендую пока его использовать. Стоит подождать пару лет, пока все браузеры начнут поддерживать приватность.
В этой части я постаралась разобрать основные принципы ООП и показала на примерах возможности JS ES>=6. Конечно же, во второй части рассмотрены не все возможности и нюансы. Но, думаю, для перехода от процедурного кода в ООП – вполне достаточно. В 3-ей части я продемонстрирую: как передавать данные от клиента на сервер с помощью микроweb-сервера, созданного с помощью python.