Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
rassen
7 лет назад

Стать программистом. Часть 31. Основы JavaScript для абсолютных новичков!

Доброго времени суток, друзья!

В наших прошлых уроках (урок №29, урок №30) мы c Вами начали рассматривать парадигму Объектно-ориентированного программирования и познакомились, пока правда только теоретически, с основными свойствами этой парадигмы: Наследованием, Полиморфизмом и Инкапсуляцией. Надеюсь основные принципы этих свойств мне удалось сделать доступными к пониманию. Если же нет, то Вы всегда можете написать об этом в комментарии, и мы вместе постараемся восполнить этот пробел.
Сегодня мы, как и было обещано в прошлом уроке, отступим от теории как от основополагающего вектора и попробуем преобразовать полученные нами знания в кодовое представление. Другими словами, мы с вами последовательно реализуем все три свойства с помощью JavaScript’a. И начнем это делать конечно же с уже знакомого нам в теоретическом плане Наследования.

Давайте, приступим!

УРОК 31. OОП. Наследование, Часть I. Оператор new.

Итак, в теории вроде бы все ясно. Наследование это когда есть Родитель, у него есть набор свойств и методов, и все Потомки, которые происходят от этого Родителя сразу же после своего возникновения получают по наследству все эти свойства и методы.
Как это всё выглядит в программировании на JavaScript’e? Для начала давайте разберем что такое Родитель и что такое Потомок. Родитель - это лекало, теплит, по образу и подобию которого мы будем создавать потомков. По своей сути Родитель как таковой участвует в программе просто как образец. А всю работу и все манипуляции мы будем проводить исключительно с его потомками. Как Родитель выглядит в JavaScript? Очень просто – это обычная функция (с небольшими нюансами) и давайте ее создадим:

/* Родитель, Класс, Функция Конструктор */
var Animal = function() {

}

Вот так обыденно мы создали функцию Animal. Давайте теперь подробнее. Обратите внимание, что в комментарии помимо слова «Родитель» я еще написал «Класс» и «Функция Конструктор». Дело в то что в JavaScript’e функция, которая потом будет использоваться для создания потомков может носит все эти названия. И в общем-то они вполне логичны. Родитель, потому что от него происходят потомки. Класс – это как в биологии помните, есть животные, а есть классы животных, как некая общность. Наконец, функция конструктор – ну можно сказать что действительно мы будем конструировать потомков по её образцу.
Обратите внимание еще и на название. Оно начинается с большой буквы. Этот нюанс не является обязательным с точки зрения исполнения кода. Он скорее общепринятый в среде разработчиков, для того чтобы человек работающий, например, с чужим кодом, сразу понимал «ага, это функция конструктор!»
Теперь давайте вспомним нашу схему Наследования из урока №29)

Как мы помним, и видим из схемы от нашего Родителя мы производим двух Потомков: это кот (cat) и пёс (dog). Давайте реализуем их в коде.

/* Родитель, Класс, Функция Конструктор */
var Animal = function() {
    
}

/* Потомки */
var cat = new Animal();
var dog = new Animal();

По сути Потомки в JavaScript’e это обычные переменные, в значение которых мы ставим вызов функции-родителя (подчеркнуто синим цветом). В самом вызове функции для Вас уже не должно быть ничего необычного, однако обратите внимание что в нашем случае мы перед непосредственным вызовом использовали новый оператор new. Именно этот оператор сообщает JavaScript’y при исполнении кода что функция Animal не просто исполняется, а исполняется как Родитель. Сам оператор переводится как «новый». И посмотрите, если поробовать перевести (примерно) строку var cat = new Animal(); то у нас выйдет что-то типа : «создать кота который равен новому животному» и тоже самое с собакой. Вроде бы все ясно и понятно. Давайте посмотрим, как выглядят наши потомки cat и dog в консоли браузера:

Мы видим, что оба наших потомка присутствуют в Глобальной Области Видимости. Если набрать имя нашего потомка в консоли и нажать enter, то мы увидим, что по сути это объект производный от нашего родителя Animal. Ну а, чтобы уж точно убедиться, что мы имеем дело именно с Объектом (Object) как типом данных мы проверим наших потомков с помощью знакомого нам из прошлых уроков оператора typeof и лишний раз убедимся, что все потомки – это Объекты.
Итак, давайте немного подытожим:

  1. Для того чтобы создать Родителя нам надо создать обычную функцию и желательно (но не обязательно) дать ей название, которое будет начинаться с большой буквы.
  2. Для того чтобы создать Потомка нам требуется создать переменную в значении которой вызвать нашу функцию-родителя с оператором new
  3. Наконец, все потомки, которые произведены от функции-родителя (класса, функции-конструктора) являются Объектами

Идем дальше. Как мы видим на схеме наш Родитель имеет два свойства (paws и tail) и три метода (walk, sleep и eat). Давайте добавим свойства к нашей функции конструктору:

/* Родитель, Класс, Функция Конструктор */
var Animal = function() {
    this.tail = 1;
    this.paws = 4;
}

/* Потомки */
var cat = new Animal();
var dog = new Animal();

Мы добавили нашему Родителю два свойства tail и paws. Обратите внимание что сделали мы это используя контекст this знакомый нам из урока №28 и подчеркнуты оранжевой линией. Давайте разберемся почему именно так, а не скажем вот таким способом используя привычный нам var:

Дело в том, что функция Animal предполагается использоваться нами как функция Родитель. И если бы мы просто определили переменные через var то при вызове они бы просто создались и все. Нам же требуется чтобы они не просто создались, но и передались потомку, причем не просто передались, а передались так чтобы для каждого Потомка они были свои собственные. И если мы вдруг захотим поменять свойство tail у кота, то на свойство tail у собаки это бы никак не повлияло. Контекст this это в данном случае переменная. В совокупности с оператором new на ее место подставится ссылка на самого потомка, как только он будет создан.
Другими словами когда мы создаем кота var cat = new Animal();, то внутри функции Animal подставляется ссылка на объект cat, когда мы создаем собаку var dog = new Animal(); то на месте контекста this уже будет находится ссылка на объект dog.
Давайте убедимся с помощью консоли, что наши Потомки получили необходимые свойства:

Как видим и у кота, и у собаки появились свойства, описанные в Родителе. Для чистоты эксперимента, давайте теперь попробуем изменить какое-либо свойство у собаки и удостоверимся, что у кота аналогичное свойство останется без изменений. Сделаем это прямо в консоли браузера:

Итак, мы присвоили хвосту собаки новое значение. Убедились, что оно поменялось (коричневые стрелки). Ну, а затем пошли к коту и поинтересовались как дела с его хвостом. И там оказалось все в порядке и без изменений (зеленая стрелка).

Значение свойств Потомков можно задавать непосредственно во время их создания. Как мы помним из прошлых уроков о функциях они могут принимать параметры. Функция-родитель (функция-конструктор, класс) в этом плане не исключение. Давайте создадим одно свойство, которого нет на нашей схеме. И передадим ему значение при создании. Назовем это свойство name:

/* Родитель, Класс, Функция Конструктор */
var Animal = function(name) {
    this.name = name;
    this.tail = 1;
    this.paws = 4;
}

/* Потомки */
var cat = new Animal(' кот "Бегемот" ');
var dog = new Animal(' собака "Барабака" ');

Обратите внимание теперь мы нашей функции-родителю передаем параметр name а нашему свойству присваиваем значение этого параметра.
Далее, при создании потомков мы при вызове функции Animal ставим на место параметра name Строки (String). Для кота - ' кот "Бегемот" ' , которая станет значением для свойства this.name именно для кота. Ну а для собаки это будет строка ' собака "Барабака" '.
Давайте зайдем в консоль и посмотрим, что у нас получилось:

Как видим и у кота, и у собаки появилось новое свойство name со значениями, которые мы передали непосредственно при создании этих потомков.
В целом, такая же логика актуальна и для создания методов. Давайте добавим те из них, которые присутствовали на нашей схеме:

Мы добавили три метода walk, sleep и eat нашему Родителю. Пока это примитивные функции, которые просто выводят сообщение о действии в консоль браузера. Давайте убедимся, что они действительно добавились и работают:

Из скриншота видно, что все три метода появились у наших Потомков и на примере метода walk (вызов метода обведен в бордовую рамку), мы видим, что они работают и для кота, и для собаки. Однако делают они одно и то же и пока не видно, являются ли они специфическими для кота и собаки. Давайте немного усовершенствуем наши методы для того чтобы было ясно кто именно выполняет то или иное действие. Для этого мы воспользуемся нашим свойством this.name, так как мы знаем, что оно разное по значению у обоих потомков:

/* Родитель, Класс, Функция Конструктор */
var Animal = function(name) {
    this.name = name;
    this.tail = 1;
    this.paws = 4;
    this.walk = function() {
        console.log(this.name + ' ходит...');
    };
    this.sleep = function() {
        console.log(this.name + ' спит...');
    };
    this.eat = function() {
        console.log(this.name + ' ест...');
    };
}

/* Потомки */
var cat = new Animal(' кот "Бегемот" ');
var dog = new Animal(' собака "Барабака" ');

Что мы сделали? А мы просто добавили в каждый метод к строке сообщения имя нашего потомка. И так как для собаки это будет «собака "Барабака"», а для кота это будет «кот "Бегемот"», то и методы у нас должны будут отработать немного по-разному. Давайте проверим:


Как видим в самих Потомках наши методы остались без изменений. Но вот уже при вызове они начали выводить разные данные.
Методам, так они являются функциями, мы также можем передавать параметры. Давайте покажем это на примере:

/* Родитель, Класс, Функция Конструктор */
var Animal = function(name) {
    this.name = name;
    this.tail = 1;
    this.paws = 4;
    this.walk = function() {
        console.log(this.name + ' ходит...');
    };
    this.sleep = function() {
        console.log(this.name + ' спит...');
    };
    this.eat = function(food) {
        console.log(this.name + ' ест ' + food);
    };
}

/* Потомки */
var cat = new Animal(' кот "Бегемот" ');
var dog = new Animal(' собака "Барабака" ');

Как видите мы немного преобразовали в Родителе метод eat и теперь как параметр мы можем передавать этому методу еду. Все мы в курсе из рекламы что любимая пища для кота это «Whiskas», а для собаки «Pedigree»
Давайте покормим наших питомцев:

И вот мы уже видим, как наши питомцы нямкают еду.

Давайте подытожим:

  1. Методы, так же, как и свойства задаются Родителю с использованием контекста this.
  2. Значения свойств могут быть объявлены при непосредственном создании Потомка если передать его значение как параметр функции-родителю
  3. Методы, так же могут принимать параметры и использовать их. Кроме того, если внутри них будет вызываться какое-либо свойство, то браться оно будет в контексте текущего потомка (пример с this.name).

На этом мы, пожалуй, закончим первую часть знакомства с Наследованием в разрезе программирования. На самом деле, то что мы сегодня просмотрели, нельзя назвать полноценным Наследованием, эти манипуляции можно определить, как создание Потомка (Объекта) по шаблону Родителя (функции-конструктора, класса). А вот работу с полноценным Наследованием мы рассмотрим в нашем следующем уроке.

А на сегодня все.

Ссылки на предыдущие уроки:

Урок 1 - Окружение.,
Урок 2 - Некоторые особенности синтаксиса.,
Урок 3 - Переменные.,
Урок 4 - Типы переменных, как основа для их взаимодействия.,
Урок 5 - Операции с переменными одного типа.,
Урок 6 - Операции с переменными одного типа. Часть II.,
Урок 7 - Взаимодействие переменных с разными типами.,
Урок 8 - Взаимодействие переменных разного типа. часть II.,
Урок 9 - Взаимодействие переменных разного типа. Часть III.,
Урок 10 - Другие возможные взаимодействия между переменными.,
Урок 11 - Другие возможные взаимодействия между переменными. Часть II.,
Урок 12 - Другие возможные взаимодействия между переменными. Операторы присваивания.,
Урок 13 - Другие возможные взаимодействия между переменными. Операторы сравнения.,
Урок 14 - Сложные переменные. Array и Object.,
Урок 15 - Условные операторы.),
Урок 16 - Циклы.,
Урок 17 - Циклы. Часть II.,
Урок 18 - Функции.,
Урок 19 - Функции. Часть II.,
Урок 20 - Профилирование. Функции, часть III.,
Урок 21 - Функции, Часть IV. Аргументы.,
Урок 22 - Objects (Объекты).,
Урок 23 - Встроенные функции и объекты.,
Урок 24 - Встроенные функции и Объекты, Часть II. Глобальные функции и переменные.,
Урок 25 - Встроенные функции и Объекты, Часть III. Document Object Model.,
Урок 26 - Встроенные функции и Объекты, Часть III. Document Object Model.
Урок 27 - Встроенные объекты. Объект Style, Events, Часть II.
Урок 28 - Встроенная переменная this. Глобальная и локальная области видимости.
Урок 29 - Объектно-ориентированное Программирование. Введение.
Урок 30. Объектно-ориентированное Программирование. Часть II.

7
339.558 GOLOS
На Golos с December 2016
Комментарии (3)
Сортировать по:
Сначала старые